bzoj 2527: [Poi2011]Meteors 整体二分

来源:互联网 发布:中兴手机mac地址 编辑:程序博客网 时间:2024/06/08 11:48

Description

有N个成员国。现在它发现了一颗新的星球,这颗星球的轨道被分为M份(第M份和第1份相邻),第i份上有第Ai个国家的太空站。

这个星球经常会下陨石雨。BIU已经预测了接下来K场陨石雨的情况。BIU的第i个成员国希望能够收集Pi单位的陨石样本。你的任务是判断对于每个国家,它需要在第几次陨石雨之后,才能收集足够的陨石。输入:第一行是两个数N,M。第二行有M个数,第i个数Oi表示第i段轨道上有第Oi个国家的太空站。第三行有N个数,第i个数Pi表示第i个国家希望收集的陨石数量。第四行有一个数K,表示BIU预测了接下来的K场陨石雨。接下来K行,每行有三个数Li,Ri,Ai,表示第K场陨石雨的发生地点在从Li顺时针到Ri的区间中(如果Li<=Ri,就是Li,Li+1,...,Ri,否则就是Ri,Ri+1,...,m-1,m,1,...,Li),向区间中的每个太空站提供Ai单位的陨石样本。输出:N行。第i行的数Wi表示第i个国家在第Wi波陨石雨之后能够收集到足够的陨石样本。如果到第K波结束后仍然收集不到,输出NIE。

数据范围:1<=n,m,k<=3*10^51<=Pi<=10^91<=Ai<10^9
  

       第一道整体二分题。

       根据自己的理解来说,整体二分就是将一个量(一般为时间),进行二分,对于已经满足的,就分到mid以左的部分递归进行二分,直到左边界等于右边界,如果没有满足就直接剪掉已经得到的部分,分到mid以右的部分进行递归实现,总体复杂度为o(每一层的复杂度)*logn。感觉在很多情况下整体二分可以将一道树套树的题简化为一道二分加树的题,降低编程难度与常数。

       话不多说,对于这道题来说,第一步找到可以进行二分的量:时间,因为随着时间增加收集到的陨石是递增的,即意味着收集好了的陨石后面也永远都是收集好的了。

       在二分的过程中,我们每次让一半的流星雨下下来,判断每个国家是否收集完成,如果已经完成,那么答案肯定在mid向左的时间,如果没有完成,就将它的需求减去现在已经收集到的,这样只需递归下mid向右时间的即可了。

       在处理给一个区间下流星的情况,由于是一个圆,所以如果给的l<=r,那么就是[l,r]这个区间下了流星雨,如果是l>r,那么就是[l,n]以及[1,r]下了流星雨了,那么如何区间加呢,最简单粗暴的方法可以使用线段树直接区间加,当然更巧妙的办法是写树状数组维护差分序列,给[l,r]下就给树状数组l的位置+1,r+1的位置-1,这样每次查询该位置的前缀和,也就是树状数组的query函数的答案,就是当前点下了多少流星雨了。

        细节注意:

       1. 每一层处理完要清空涉及到的数组的部分,如果直接memset复杂度会极大,

       2. 如果一个国家已经收集满了就不用收了,这样可以防止答案爆long long,

       3. dfs中传vector固然方便,但是满了接近10倍,在处理位置的时候,我们可以开一个辅助的id数组,记录当前这个区间有哪些数字即可,

       4. 由于有些国家可能收集不满答案也会被更新到n,所以最后下一场无穷大的流星雨,如果答案在这一场,即证明收集不满。

      下附AC代码

#include<iostream>#include<stdio.h>#include<string.h>#include<algorithm>#include<vector>#define mid ((nl+nr)>>1)#define maxn 300005using namespace std;typedef long long ll;inline int read(){    int x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}//以下为基本数组 int n,m,k;int res[maxn];int id[maxn],temp[maxn];int bel[maxn],ned[maxn];int z[maxn],y[maxn],t[maxn];vector<int>edge[maxn];//以下为树状数组 ll dat[maxn];int lowbit(int now){return (now&(-now));}void update(int now,int add){for(int i=now;i<=m;i+=lowbit(i)){dat[i]+=add;}}ll query(ll pos){ll ans=0;for(int i=pos;i>=1;i-=lowbit(i)){ans+=dat[i];}return ans;}void insert(int now,int op){if(z[now]<=y[now]){update(z[now],op*t[now]);update(y[now]+1,-op*t[now]);}else{update(z[now],op*t[now]);update(1,op*t[now]);update(y[now]+1,-op*t[now]);}}ll sum[maxn];void dfs(int ql,int qr,int nl,int nr){if(ql>qr) return;if(nl==nr){for(int i=ql;i<=qr;i++){res[id[i]]=nl;}return;}for(int i=nl;i<=mid;i++){insert(i,1);}for(int i=ql;i<=qr;i++){sum[id[i]]=0;}for(int i=ql;i<=qr;i++)    {    int len1=edge[id[i]].size();    for(int j=0;j<len1;j++)        {        sum[id[i]]+=query(edge[id[i]][j]);        if(sum[id[i]]>=ned[id[i]]) break;}    }int cnt=0;for(int i=ql;i<=qr;i++){if(sum[id[i]]>=ned[id[i]])cnt++;}int l1=ql,l2=ql+cnt;for(int i=ql;i<=qr;i++){if(sum[id[i]]>=ned[id[i]]){temp[l1++]=id[i];}else{temp[l2++]=id[i];ned[id[i]]-=sum[id[i]];}}for(int i=ql;i<=qr;i++)id[i]=temp[i];for(int i=nl;i<=mid;i++){insert(i,-1);}dfs(ql,ql+cnt-1,nl,mid);dfs(ql+cnt,qr,mid+1,nr);}int main(){n=read();m=read();    for(int i=1;i<=m;i++)    {    int x;    x=read();    edge[x].push_back(i);    }for(int i=1;i<=n;i++){ned[i]=read();id[i]=i;}k=read();for(int i=1;i<=k;i++){z[i]=read();y[i]=read();t[i]=read();}k++;z[k]=1;y[k]=m;t[k]=1000000009;dfs(1,n,1,k);for(int i=1;i<=n;i++){if(res[i]!=k) printf("%d\n",res[i]);else printf("NIE\n");}} 





原创粉丝点击