【[POI2011]MET-Meteors】整体二分+一些神奇优化
洛谷上很早就过了,然而NK上的数据很强,迫不得已重构了两次代码orz,还调整了一下算法。
luogu3752
P3932Meteors | ||
|
问题描述
Byteotian Interstellar Union有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。
样例输入
3 5
1 3 2 1 3
10 5 7
3
4 2 4
1 3 1
3 5 2
样例输出
3
NIE
1
提示
1<=n,m,k<=3*10^5 1<=Pi<=10^9 1<=Ai<10^9
来源 POI2011
这道题有个十分显然的做法,就是直接套入一个树状差分数组进行整体二分,然后每次加入前mid个,查询,搞完还原树状数组。看上去时间复杂度并没有什么问题,然后,愉快的被卡卡卡卡常(微笑)。
这时候就要用到一个类似于莫队里面的左移右移的东西了。我们可以将下陨石和询问分开看,设定一个类似莫队R的 东西(本题是前缀和没有L),对R跑到当前mid的位置。这样我们可以尝试去画一个图,就会惊讶发现,对于整个整体二分最多每层会移动n次。
看似对时间复杂度影响不大,其实不然,实际上我们反复用了上一次的插入树状数组后的结果,也就是说,我们节省了很多重复的,冗杂的工作,最后写出代码也其实更加精简易懂(不用处理队列类型)。
code:
#include<stdio.h> #include<bits/stdc++.h> #define lowbit(x) ((x)&(-x)) using namespace std; const int maxn = 300005; int n,m,k; int bit[maxn]; vector<int>ve[maxn]; void add(int x,int d) { for(;x<=m+2;x+=lowbit(x)) bit[x]+=d; } int getsum(int x) { int sum=0; for(;x;x-=lowbit(x)) sum+=bit[x]; return sum; } int ans[maxn]; struct node { int id,ned; }; struct chan { int l,r,cha; }z[maxn]; void modify(int po,int knd) { if(knd) { if(z[po].r>=z[po].l) { add(z[po].l,z[po].cha); add(z[po].r+1,-z[po].cha); } else { add(1,z[po].cha); add(z[po].r+1,-z[po].cha); add(z[po].l,z[po].cha); } } else { if(z[po].r>=z[po].l) { add(z[po].l,-z[po].cha); add(z[po].r+1,z[po].cha); } else { add(1,-z[po].cha); add(z[po].r+1,z[po].cha); add(z[po].l,-z[po].cha); } } } int cur; void query(vector<node>q,int l,int r) { if(!q.size()) return; if(l==r) { int ss = q.size(); for(int i=0;i<ss;i++) ans[q[i].id] = l; return; } int mid = (l+r)/2; while(cur<mid) ++cur,modify(cur,1); while(cur>mid) modify(cur,0),--cur; int ss=q.size(); vector<node>q1,q2; for(int i=0;i<ss;i++) { int x = q[i].id; int sd = ve[x].size(); int summ = 0; for(int j=0;j<sd;j++) { summ+=getsum(ve[x][j]); if(summ>=q[i].ned) break; } if(summ>=q[i].ned) q1.push_back(q[i]); else q2.push_back((node){q[i].id,q[i].ned}); } query(q1,l,mid); query(q2,mid+1,r); } int main() { vector<node>q; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int x; scanf("%d",&x); ve[x].push_back(i); } for(int i=1;i<=n;i++) { int x; scanf("%d",&x); q.push_back((node){i,x}); } scanf("%d",&k); for(int i=1;i<=k;i++) { int ll,rr,aa; scanf("%d%d%d",&ll,&rr,&aa); z[i].l = ll ;z[i].r = rr; z[i].cha = aa; } ++k; z[k].l=1; z[k].r=m; z[k].cha = 0x3f3f3f3f; query(q,1,k); for(int i=1;i<=n;i++) if(ans[i]!=k) printf("%d\n",ans[i]); else printf("NIE\n"); }