ZOJ 3940 Modulo Query(优先队列)

来源:互联网 发布:怎样加入淘宝客推广 编辑:程序博客网 时间:2024/06/04 01:31

省赛题解:http://async.icpc-camp.org/d/405-2016

E. MODULO QUERY

可以观察到一个区间对一个数取模后的结果也可以用区间来表示,并且这些区间的左端点都是0。于是直接用一个map存每次取模之后的区间和这个区间出现次数,要取模的时候,找出所有右端点大于等于当前模数的所有区间,暴力算一下结果即可。对于查询,二分下位置,求个后缀和就好了。下面分析下复杂度为什么是对的。

众所周知:一个数对一堆数取模,最多会有loglog次值的改变。对于这题每对一个新数取模,最多只会增加一个区间。考虑当前区间是0vi[0,vi],要对xx取模,那么对于vixvi<x的区间没有变化,对于vixvix的那些区间会变成0vix[0,vi mod x],并且可能会新增区间0x1[0,x1]。这个过程其实相当于每个aiai对后面所有数依次取模,那么总共会有OnlognO(nlogn)次值的改变,每次改变需要在map上修改一下,总复杂度是Onlog2nO(nlog2n)的。

//题意:自行理解#include <iostream>#include <string.h>#include <queue>#include <climits>#include <algorithm>using namespace std;#define maxn 500005const int mod = 1e9+7;int sum[maxn];int ans[maxn];struct node{   // 区间[0, right]以及该区间出现的次数cnt    int right, cnt;    node(int a = 0, int b = 0)    {        right = a, cnt = b;    }    bool operator<(const node &a) const    {        return right < a.right;    }}tp[100005];int main(){    int t;    cin>>t;    while(t--)    {        int n, m, s;        cin>>n>>m;        priority_queue<node> que; //优先队列        que.push(node(m, 1)); //入队        while(n--)        {            cin>>s;            while(que.top().right >=  s)  //将区间右断点大于s的区间进行mod处理            {                node tmp = que.top();                     que.pop();                while(!que.empty() && que.top().right == tmp.right)   //如果有重复的区间进行整合                    tmp.cnt += que.top().cnt, que.pop();                que.push(node(s - 1, (tmp.right + 1)/ s * tmp.cnt));  //将处理过后的区间入队                if((tmp.right + 1)% s)                    que.push(node(tmp.right%s, tmp.cnt));  //如果一个区间不能被整除  将多余的区间入队                            }        }        int cnt = 1;        while(!que.empty()) //将最后得到的所有区间存在一个数组中        {            node tmp = que.top();            que.pop();            while(!que.empty() && que.top().right == tmp.right)                tmp.cnt += que.top().cnt, que.pop();            tp[cnt++] = tmp;        }        sort(tp + 1, tp + cnt); //将各个区间进行从小到大排序        memset(sum, 0, sizeof sum);  //初始化为0        for(int i = 1; i < cnt; i++)        {            ans[i] = tp[i].right;            sum[cnt - i] = sum[cnt - i + 1] + tp[cnt - i].cnt;        }        int q;        cin>>q;        long long out = 0;        for(int i = 1; i <= q; i++)        {            cin>>s;            int pos = lower_bound(ans + 1, ans + cnt, s) - ans; //二分查找结果            out = (out + 1ll * i * sum[pos] % mod) % mod;        }        cout<<out<<endl;    }    return 0;}



0 0
原创粉丝点击