[Poi2011] Meteors(从不知所措到整体二分)
Byteotian Interstellar Union (BIU) has recently discovered a new planet in a nearby galaxy. The planet is unsuitable for colonisation due to strange meteor showers, which on the other hand make it an exceptionally interesting object of study.
The member states of BIU have already placed space stations close to the planet's orbit. The stations' goal is to take samples of the rocks flying by. The BIU Commission has partitioned the orbit into sectors, numbered from to , where the sectors and are adjacent. In each sector there is a single space station, belonging to one of the member states.
Each state has declared a number of meteor samples it intends to gather before the mission ends. Your task is to determine, for each state, when it can stop taking samples, based on the meter shower predictions for the years to come.
Input
The first line of the standard input gives two integers, and (), separated by a single space, that denote, respectively, the number of BIU member states and the number of sectors the orbit has been partitioned into.
In the second line there are integers (), separated by single spaces, that denote the states owning stations in successive sectors.
In the third line there are integers (), separated by single spaces, that denote the numbers of meteor samples that the successive states intend to gather.
In the fourth line there is a single integer () that denotes the number of meteor showers predictions. The following lines specify the (predicted) meteor showers chronologically. The -th of these lines holds three integers , , (separated by single spaces), which denote that a meteor shower is expected in sectors (if ) or sectors (if ), which should provide each station in those sectors with meteor samples ().
Output
Your program should print lines on the standard output. The -th of them should contain a single integer , denoting the number of shower after which the stations belonging to the -th state are expected to gather at least samples, or the word NIE (Polish for no) if that state is not expected to gather enough samples in the foreseeable future.
Example
For the input data:
3 5 1 3 2 1 3 10 5 7 3 4 2 4 1 3 1 3 5 2
the correct result is:
3 NIE 1
题意:
n个国家,m个收集站,每个收集站分别归属于某个国家,一共又k颗流星,问每个国家再第几颗流星经过可以搜集够自己需要的流星need[i]。
先谈谈自己的感受。
普通二分过程([L,R]+一个询问X):二分[L,mid]或者二分[mid+1,R]。最后L==R时得到了一个询问的答案X。
整体二分过程([L,R]+一群询问X):部分询问(集合A)需要二分[L,mid](假设叫左二分),一些询问(集合B)需要二分[mid+1,R](右二分)。这个时候把属于左二分是一起左二分,属于有二分的一起右二分。 当L==R时得到了当前集合的答案,其中集合X=集合A+集合B。
对于整体效率的分析:
假设二分solve(X,L,R)表示集合X的答案在[L,R]间(L,R是由上一层二分判定缩小的范围,初始L=1,R=m,X为1到n的集合)。
按照上面整体二分的过程,solve(X,L,R)继续分解为solve(A,L,mid)和solve(B,mid+1,R)两部分;前面一部分是mid的时候,A集合满足条件need[],答案范围缩小为[L,mid];同理,B集合不满足条件need[],答案范围缩小到[mid+1,R]。
所以,若最开始的集合X=(1,2,3,...n)最后分解为了g个集合(1<=g<=m,m为操作数量,此题的流星数量),
每一个二分复杂度为O(qlgn),q为一个常数,大小取决于题目,总的效率为O(g*qlgn)<O(m*qlgn)。如果最后集合g,q不会太大,整体二分就行得通。而g取决于数据,q取决于算法。
综上,大概知道什么是整体二分了。具体针对这道题:
(抄袭的代码,From victorywonder,现在主要是学习思想,首先的化,多做几个题再整理)。
#include<cstdio> #include<algorithm> #include<iostream> #include<cstring> using namespace std; typedef long long ll; typedef double D; typedef pair<int,int> pr; const int infi=1000000010; const int N=500010; const int M=2000100; struct node{int x,y,z;}p[N]; int n,m,k,c[N],id[N],ans[N]; int g[N],to[N],nxt[N],tot; int tol[N],tor[N]; ll h[N],tmp[N],cur[N]; void add(int pos,int x) {while (pos<=m) h[pos]+=x,pos+=(pos&-pos);} void adddt(int x,int y,int z) {add(x,z); add(y+1,-z);} ll sum(int pos) {ll t=0; while (pos>0) t+=h[pos],pos-=(pos&-pos);return t;} void addop(int x,int y,int z,int i) {p[i].x=x; p[i].y=y; p[i].z=z;}//第i颗流星 void addpt(int x,int y) {to[++tot]=y; nxt[tot]=g[x]; g[x]=tot;}//第x个国家,拥有的收集站。 void solve(int head,int tail,int l,int r) { //id [head到tail]的队列答案在[l,r]中 if (head>tail) return; int i,k,x,mid=(l+r)>>1,lnum=0,rnum=0; if(l==r) { for (i=head;i<=tail;i++) ans[id[i]]=l; return; } for(i=l;i<=mid;i++) {//试探左半截。 if (p[i].x<=p[i].y) adddt(p[i].x,p[i].y,p[i].z);//加入树状数组 else adddt(p[i].x,m,p[i].z),adddt(1,p[i].y,p[i].z);//由于是个环。 } for(i=head;i<=tail;i++) { tmp[id[i]]=0; for (k=g[id[i]];k;k=nxt[k]) {//第id[i]个国家的每一个站。 tmp[id[i]]+=sum(to[k]); if (tmp[id[i]]+cur[id[i]]>c[id[i]]) break; } if (cur[id[i]]+tmp[id[i]]>=c[id[i]]) tol[++lnum]=id[i]; else tor[++rnum]=id[i],cur[id[i]]+=tmp[id[i]]; } for (i=l;i<=mid;i++) {//减回去。效果就是memset(h,0,sizeof(h)),但是memset太浪费了; if (p[i].x<=p[i].y) adddt(p[i].x,p[i].y,-p[i].z); else adddt(p[i].x,m,-p[i].z),adddt(1,p[i].y,-p[i].z); } for (i=0;i<lnum;i++) id[head+i]=tol[i+1]; for (i=0;i<rnum;i++) id[head+lnum+i]=tor[i+1]; solve(head,head+lnum-1,l,mid); solve(head+lnum,tail,mid+1,r); } int main() { int i,x,y,z; scanf("%d%d",&n,&m); for (i=1;i<=m;i++) { scanf("%d",&x); addpt(x,i); } for (i=1;i<=n;i++) { scanf("%d",&c[i]); id[i]=i; } scanf("%d",&k); for (i=1;i<=k;i++) { scanf("%d%d%d",&x,&y,&z); addop(x,y,z,i); } addop(1,m,infi,++k); solve(1,n,1,k); for (i=1;i<=n;i++) if (ans[i]!=k) printf("%d\n",ans[i]); else puts("NIE"); return 0; }