CFgym:Memory Manager(dp & 单调队列优化)

来源:互联网 发布:ug怎么编程 编辑:程序博客网 时间:2024/06/08 03:30

"D" Memory Manager
Time limit3 secondsMemory limit256 megabytes

Peter is developing memory manager MEM 2.0 for his offline storage that uses special magnetic 7D blocks. But he has a problem of accessing data in the optimal way.

Peter's memory manager stores n blocks of data, numbered from 1 to n, and he has q queries of accessing one or several of the blocks. Queries must be processed in order they are listed.

To access the data Peter's memory manager has k pointers, each of them points to some block. Initially Peter can position his pointers at any desired blocks.

MEM 2.0 can immediately access data from any number blocks if each of them is currently has some pointer at it. If it is not the case, the manager must first move the pointers, this operation for the i-th query takes si milliseconds total to move any number of pointers.

Peter wants to move pointers in such way that the total time of answering all queries was minimal possible. Queries must be processed in order they are listed, changing the order is not allowed. Help him!

Consider sample test cases.

In the first sample test Peter can initially position pointers at blocks 1, 2 and 4 — after that the first two queries are accessed immediately. Before the third query the pointers must be moved to blocks 2, 3 and 5, it takes s3 = 1 milliseconds, and moving pointers to blocks 1, 3, and 5 before the fourth query takes another s4 = 1 milliseconds. The total time is s3 + s4 = 2 milliseconds.

The second sample test shows that it is sometimes not optimal to make a greedy choice. It is best not to perform two first queries immediately by positioning pointers at 1, 2 and 4 initially, because moving the pointers before the third query would then take 10 milliseconds. The optimal strategy is to first position pointers at blocks 1, 2 and 3, before the second query move them to blocks 1, 3 and 4 in s2 = 1 milliseconds, and then move them to 1, 3 and 5 in s4 = 3 milliseconds before the fourth query. The total time is s2 + s4 = 4 milliseconds.

Input format

Input data contains several test cases. The first line contains one integer t — the number of test cases (1 ≤ t ≤ 1000).

Each of the following t test cases is described in the following way. The first line of the description contains three integers: nkq — the number of blocks, the number of pointers and the number of queries (1 ≤ k ≤ n ≤ 105, 1 ≤ q ≤ 106).

The following line contains q integers si — time needed to move pointers if it is performed before the i-th query (1 ≤ si ≤ 104).

The following q lines contain queries in order they must be processed, the i-th query is described by a line that first contains ci — the number of blocks requested, (1 ≤ ci ≤ k), followed by ci integers bi, j — the numbers of these blocks, given in ascending order (1 ≤ bi, j ≤ n).

It is guranteed that the sum of all n of one input data doesn't exceed 105, and the sum of all ci in all test cases of one input data doesn't exceed 106.

Output format

For each test case print one integer — the minimal total time to answer all queries.

Examples
Input data
25 3 41 1 1 11 22 1 42 2 33 1 3 55 3 41 1 10 31 22 1 42 1 33 1 3 5
Output data
24
题意:N个点,K个指针,Q个任务,每个任务需要读取相应点上的内容,每个任务有一个“指针转换时间”,问初始指针位置最优安排下按顺序完成所有任务的最小时间。

思路:dp[i]为完成前i个任务的最短时间,那么完成第i个任务,最早在第几个任务需要转换指针呢?从第i个任务往回跑,记录需要的不同点数,当超过K表示最迟在该任务处要转换指针,记为pre[i]=j,那么在j~i这段任务内转换指针都有可能是最优解,dp[i] = min(dp[k=pre[i]-1...i-1]+c[k+1]),显然dp[k]+c[k+1]是重复使用的,可以用单调队列优化。

# include <bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 1e6+13;vector<int>v[maxn];multiset<pair<LL,int> >s;int p[maxn], c[maxn], vis[maxn];LL dp[maxn];int main(){    int t, n, k, q, m, tmp;    scanf("%d",&t);    while(t--)    {        memset(vis, 0, sizeof(vis));        s.clear();        scanf("%d%d%d",&n,&k,&q);        for(int i=1; i<=q; ++i) scanf("%d",&c[i]);        for(int i=1; i<=q; ++i)        {            v[i].clear();            scanf("%d",&m);            while(m--)            {                scanf("%d",&tmp);                v[i].push_back(tmp);            }        }        int h=1, num=0;        for(int i=1; i<=q; ++i)        {            for(int j=0; j<v[i].size(); ++j)                if(++vis[v[i][j]]==1) ++num;            if(num <= k) p[i] = h;            else            {                for(int j=h; j<i; ++j)                {                    for(int k=0; k<v[j].size(); ++k)                        if(--vis[v[j][k]] == 0) --num;                    if(num <= k)                    {                        h = j+1;                        p[i] = h;                        break;                    }                }            }        }        for(int i=1; i<=q; ++i)        {            if(p[i]==1)            {                dp[i] = 0;                s.insert(make_pair(dp[i]+c[i+1], i));            }            else            {                while(!s.empty() && s.begin()->second < p[i]-1)                    s.erase(s.begin());                dp[i] = s.begin()->first;                s.insert(make_pair(dp[i]+c[i+1], i));            }        }        printf("%lld\n",dp[q]);    }    return 0;}close



原创粉丝点击