标签:
Cdq分治和整体二分是两个很奇妙的东西。他们都是通过离线的思想来进行优化,从而更快的求出解。
整体二分通俗的讲就是二分答案,但是它了不起的地方是一下子把所有的答案都二分出来了,从而可以一下子得出所有查询。
CDQ分治通俗的讲就是二分查询。通常的做法是把所有的查询分成两半,然后通过递归先计算出左边一半的所有的查询,然后通过这些已知的左半边的值来更新右半边的值。这里,最最重要的思想是通过左半边来更新右半边。更具体一点,就是用左半边的修改来更新右半边的查询。
重要的事情说话三遍:
CDQ分治就是通过左半边的修改来更新右半边的查询!
CDQ分治就是通过左半边的修改来更新右半边的查询!
CDQ分治就是通过左半边的修改来更新右半边的查询!
CDQ的主要作用就是降维。因为当你二分查询的时候,你可以保证左半边的查询都满足你二分前的顺序。
一、整体二分
1. 最经典的题目:带修改的区间第k小数(hdu5412)
因为相同的题目太多了,挑了一道比较新的题。其实就是题面不一样。
题目意思:
给你N个数,有两种询问。一个是修改第K个数的值,二是询问一个区间内的第k小的数,输出这个数。
所以,具体的做法就是二分答案。如果比猜测的数字小的个数比较多,那么答案一定在左边的区间内,反之就在右边的区间内。然后递归来继续这个操作。
这是写的第一道整体二分,不要问我为什么和网上的代码几乎一模一样。我只是因为看了很久才看懂,所以几乎就背出来了……
代码:
#include <iostream> #include <sstream> #include <ios> #include <iomanip> #include <functional> #include <algorithm> #include <vector> #include <string> #include <list> #include <queue> #include <deque> #include <stack> #include <set> #include <map> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <climits> #include <cctype> #define INF 0x3f3f3f3f #define MP(X,Y) make_pair(X,Y) #define PB(X) push_back(X) #define REP(X,N) for(int X=0;X<N;X++) #define REP2(X,L,R) for(int X=L;X<=R;X++) #define DEP(X,R,L) for(int X=R;X>=L;X--) #define CLR(A,X) memset(A,X,sizeof(A)) #define IT iterator #define M_PI 3.14159265358979323846 #define _ ios_base::sync_with_stdio(0);cin.tie(0); #define X first #define Y second #define MAX_V 10101 #define maxn 323400 #define lowbit(X) (X & (-X)) #include<ctime> using namespace std; typedef long long ll; typedef pair<int,int>PII; typedef pair<PII,int>PPI; int dat[maxn]; void add(int k,int val){ while(k<maxn){ dat[k]+=val; k+=lowbit(k); } } int query(int k){ int sum=0; while(k>0){ sum+=dat[k]; k-=lowbit(k); } return sum; } struct { //sign x query val k int s,x,val,q,k,l,r,cur; }q[maxn],q1[maxn],q2[maxn]; int A[maxn]; int temp[maxn]; int ans[maxn]; //×ó±ÕÓÒ±Õ void div_solve(int head,int tail,int l,int r){ //cout<<head<<‘ ‘<<tail<<‘ ‘<<l<<‘ ‘<<r<<endl; if(head>tail)return ; if(l==r){ for(int i=head;i<=tail;++i){ if(q[i].s==3) ans[q[i].q]=l; } return ; } int mid=l+r>>1; for(int i=head;i<=tail;++i){ if(q[i].s==1 && q[i].val<=mid) add(q[i].x,1); else if(q[i].s==2 && q[i].val<=mid) add(q[i].x,-1); else if(q[i].s==3) temp[i]=query(q[i].r)-query(q[i].l-1); } for(int i=head;i<=tail;++i){ if(q[i].s==1 && q[i].val<=mid) add(q[i].x,-1); else if(q[i].s==2 && q[i].val<=mid) add(q[i].x,1); } int l1=0,l2=0; for(int i=head;i<=tail;++i){ if(q[i].s==3){ if(q[i].cur+temp[i]>=q[i].k){ q1[l1++]=q[i]; } else{ q[i].cur+=temp[i]; q2[l2++]=q[i]; } } else { if(q[i].val<=mid){ q1[l1++]=q[i]; } else{ q2[l2++]=q[i]; } } } for(int i=0;i<l1;++i){ q[i+head]=q1[i]; } for(int i=0;i<l2;++i){ q[i+head+l1]=q2[i]; } div_solve(head,head+l1-1,l,mid); div_solve(head+l1,tail,mid+1,r); } int main() { int n; while(~scanf("%d",&n)){ int a,b,c,d; int cnt=0; for(int i=1;i<=n;++i){ scanf("%d",&a); A[i]=a; q[cnt].s=1;q[cnt].x=i; q[cnt].cur=0; q[cnt++].val=a; } int t,tt=0; scanf("%d",&t); for(int i=1;i<=t;++i){ scanf("%d",&a); if(a==1){ scanf("%d%d",&b,&c); q[cnt].s=2;q[cnt].x=b;q[cnt].cur=0; q[cnt++].val=A[b]; q[cnt].s=1;q[cnt].x=b;q[cnt].cur=0; q[cnt++].val=c; A[b]=c; } else{ scanf("%d%d%d",&a,&b,&c); q[cnt].s=3;q[cnt].l=a;q[cnt].q=++tt; q[cnt].cur=0; q[cnt].r=b;q[cnt++].k=c; } } div_solve(0,cnt-1,0,INF); for(int i=1;i<=tt;++i) printf("%d\n",ans[i]); } return 0; }
2.POI 2011 Meteors
//因为bzoj现在都要收费了,找了半天终于找到免费的。
http://main.edu.pl/en/archive/oi/18/met
题目意思:
一个行星有一条圆形的轨道。这条轨道被分成了m份,这些轨道分别属于n个国家。一个国家可以拥有多个轨道。在轨道周围会有流星雨落下,现在各个国家需要收集一些流星雨。问你需要经过多少时间,各个国家才能完成他们的收集任务。
因为问的是时间,所以第一个想到是二分时间。接下来在维护一个线段树,用来区间修改。然后再根据二分时间,检查是否满足要求。继续二分,直到找到解。其中的一个处理是在t+1的时间再加一个无限大的流星,用来确定是不是能够成功收集。
代码:
#include <iostream> #include <sstream> #include <ios> #include <iomanip> #include <functional> #include <algorithm> #include <vector> #include <string> #include <list> #include <queue> #include <deque> #include <stack> #include <set> #include <map> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <climits> #include <bitset> #include <cctype> #define INF 0x3f3f3f3f #define MP(X,Y) make_pair(X,Y) #define PB(X) push_back(X) #define REP(X,N) for(int X=0;X<N;X++) #define REP2(X,L,R) for(int X=L;X<=R;X++) #define DEP(X,R,L) for(int X=R;X>=L;X--) #define CLR(A,X) memset(A,X,sizeof(A)) #define IT iterator #define M_PI 3.14159265358979323846 #define _ ios_base::sync_with_stdio(0);cin.tie(0); #define X first #define Y second #define MAX_V 10101 #define maxn 301234 #define lowbit(X) (X & (-X)) #include<ctime> using namespace std; typedef long long ll; typedef pair<int,int>PII; typedef pair<PII,int>PPI; const ll INF2=(0x3f3f3f3f)*100ll; //bit Çø¼äÐÞ¸Ä µ¥µã²éѯ ll dat[maxn]; //×ó±ÕÓÒ¿ª void init(){ memset(dat,0,sizeof(dat)); } void add(int l,int r,int val){ while(l<maxn){ dat[l]+=val; l+=lowbit(l); } while(r<maxn){ dat[r]-=val; r+=lowbit(r); } } ll query(int k){ ll sum=0; while(k>0){ sum+=dat[k]; k-=lowbit(k); } return sum; } struct edge{ int to,next; }G[maxn]; int head[maxn]; void add_edge(int from,int to,int &cnt){ edge e; e.to=to;e.next=head[from]; G[++cnt]=e; head[from]=cnt; } struct node{ int l,r,num; }Q[maxn]; ll cur[maxn]; int A[maxn],ID[maxn],T[maxn],Ans[maxn]; int n,m,t; int temp[maxn],temp2[maxn]; void solve(int start,int tail,int l,int r){ if(start>tail) return ; if(l==r){ for(int i=start;i<=tail;++i){ int d=ID[i]; Ans[d]=l; } return ; } //init(); int mid=(l*1ll+r)>>1; for(int i=l;i<=mid;++i){ int l=Q[i].l,r=Q[i].r,num=Q[i].num; if(l>r){ add(l,m+1,num); add(1,r+1,num); } else add(l,r+1,num); } int l1=0,l2=0; for(int i=start;i<=tail;++i){ int d=ID[i]; ll cnt=0; for(int j=head[d];j;j=G[j].next){ int to=G[j].to; cnt+=query(to); if(cnt>INF)break; } if(cnt+cur[d]>=T[d]){ temp[l1++]=d; } else{ temp2[l2++]=d; cur[d]+=cnt; } } for(int i=0;i<l1;++i){ ID[start+i]=temp[i]; } for(int i=0;i<l2;++i){ ID[start+l1+i]=temp2[i]; } for(int i=l;i<=mid;++i){ int l=Q[i].l,r=Q[i].r,num=Q[i].num; if(l>r){ add(l,m+1,-num); add(1,r+1,-num); } else add(l,r+1,-num); } //cout<<l<<" "<<r<<endl; solve(start,start+l1-1,l,mid); solve(start+l1,tail,mid+1,r); } int Scan(){ int res=0, ch; ch=getchar(); //windows»Ø³µ\r\n linux»Ø³µ\n ·ÀÖ¹Êý¾ÝÖÐûÓÐÈ¥³ý\r if(ch==10) ch=getchar(); if(ch>=‘0‘&&ch<=‘9‘) res=ch-‘0‘; else return -1; while((ch=getchar())>=‘0‘&&ch<=‘9‘) res=res*10+ch-‘0‘; return res; } void Out(ll a){ if(a>9) Out(a/10); putchar(a%10+‘0‘); } int main() { //scanf("%d%d",&n,&m); n=Scan();m=Scan(); memset(head,0,sizeof(head)); int tot=0; for(int i=1;i<=m;++i){ //scanf("%d",&A[i]); A[i]=Scan(); add_edge(A[i],i,tot); } for(int i=1;i<=n;++i){ //scanf("%d",&T[i]); T[i]=Scan(); ID[i]=i; } //scanf("%d",&t); t=Scan(); int l,r,num; for(int i=1;i<=t;++i){ //scanf("%d%d%d",&l,&r,&num); l=Scan();r=Scan();num=Scan(); Q[i].l=l;Q[i].r=r; Q[i].num=num; } t+=1; Q[t].num=INF;Q[t].l=1;Q[t].r=m; //memset(cur,0,sizeof(cur)); solve(1,n,1,t); for(int i=1;i<=n;++i){ if(Ans[i]==t){ puts("NIE"); } else{ //printf("%d\n",Ans[i]); Out(Ans[i]); putchar(‘\n‘); } } return 0; }
其他学习资料:http://www.cnblogs.com/zig-zag/archive/2013/04/18/3027707.html
二、CDQ分治
1.Boring Class(hdu5324)
题目意思:给你两个序列,分别为L序列和R序列。求一个最长的序列,满足对于每个Vi,Vj,L[Vi]>=L[Vj] 并且R[Vi]<=R[Vj],同时还要的是字典序最小这个条件。
这题是典型的CDQ分治的题目。因为有三维,顺序维、L维和R维,我们不好处理。所以这个时候就可以通过CDQ分治来二分它的顺序。这样就可以保证在CDQ的过程中,时间顺序一定是满足的。接下来在CDQ中,我们不妨按照R来排序,这样可以确定R也满足顺序,然后再进行DP就可以得出结果了。其中要注意的是从右往左DP,这样可以求出字典序的最小。
代码:
#include <iostream> #include <sstream> #include <ios> #include <iomanip> #include <functional> #include <algorithm> #include <vector> #include <string> #include <list> #include <queue> #include <deque> #include <stack> #include <set> #include <map> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <climits> #include <bitset> #include<cassert> #include <cctype> #define NINF -0x3f3f3f3f #define MP(X,Y) make_pair(X,Y) #define PB(X) push_back(X) #define REP(X,N) for(int X=0;X<N;X++) #define REP2(X,L,R) for(int X=L;X<=R;X++) #define DEP(X,R,L) for(int X=R;X>=L;X--) #define CLR(A,X) memset(A,X,sizeof(A)) #define IT iterator #define M_PI 3.14159265358979323846 #define _ ios_base::sync_with_stdio(0);cin.tie(0); #define X first #define Y second #define MAX_V 10101 #define maxn 50099 #define lowbit(X) (X & (-X)) #include<ctime> using namespace std; typedef long long ll; typedef pair<int,int>PII; typedef pair<PII,int>PPI; int tot; int L[maxn],R[maxn]; int Num[maxn*2],D[maxn]; struct node{ int l,r,id; bool operator<(const node &B)const{ if(r!=B.r)return r<B.r; if(l!=B.l)return l>B.l; return id<B.id; } }q1[maxn],q2[maxn]; int dat[maxn*2]; void update(int k,int val){ while(k<tot+10){ dat[k]=max(dat[k],val); k+=lowbit(k); } } void clear(int k){ while(k<tot+10){ dat[k]=0; k+=lowbit(k); } } int query(int k){ int Max=0; while(k>0){ Max=max(Max,dat[k]); k-=lowbit(k); } return Max; } void cdq(int l,int r){ if(l==r)return; int mid=(l+r)/2; cdq(mid+1,r); for(int i=l;i<=mid;++i){ q1[i].l=L[i];q1[i].r=R[i];q1[i].id=i; } for(int i=mid+1;i<=r;++i){ q2[i].l=L[i];q2[i].r=R[i];q2[i].id=i; } sort(q1+l,q1+mid+1); sort(q2+mid+1,q2+r+1); for(int i=mid,j=r;i>=l;--i){ while(j>mid && q2[j].r>=q1[i].r){ update(q2[j].l,D[q2[j].id]); j--; } D[q1[i].id]=max(D[q1[i].id],query(q1[i].l)+1); } for(int i=mid+1;i<=r;++i){ clear(q2[i].l); } cdq(l,mid); } int Scan(){ int res=0, ch; ch=getchar(); //windows»Ø³µ\r\n linux»Ø³µ\n ·ÀÖ¹Êý¾ÝÖÐûÓÐÈ¥³ý\r if(ch==10) ch=getchar(); if(ch>=‘0‘&&ch<=‘9‘) res=ch-‘0‘; else return -1; while((ch=getchar())>=‘0‘&&ch<=‘9‘) res=res*10+ch-‘0‘; return res; } void Out(ll a){ if(a>9) Out(a/10); putchar(a%10+‘0‘); } int main() { int n; //~(n=Scan()) //freopen("data.in","r",stdin); while(~scanf("%d",&n)){ tot=0; for(int i=1;i<=n;++i){ scanf("%d",&L[i]); //L[i]=Scan(); Num[tot++]=L[i]; } for(int i=1;i<=n;++i){ scanf("%d",&R[i]); //R[i]=Scan(); Num[tot++]=R[i]; } sort(Num,Num+tot); tot=unique(Num,Num+tot)-Num; for(int i=1;i<=n;++i){ L[i]=(lower_bound(Num,Num+tot,L[i])-Num)+1; R[i]=(lower_bound(Num,Num+tot,R[i])-Num)+1; } // cout<<"ok"<<endl; for(int i=1;i<maxn;++i) D[i]=1; cdq(1,n); int ans=0; for(int i=1;i<=n;++i){ ans=max(D[i],ans); } printf("%d\n",ans); //Out(ans);putchar(‘\n‘); int pre=0; for(int i=1;i<=n;++i){ if(D[i]==ans && (pre==0 || ( L[pre]>=L[i] && R[pre]<=R[i]))){ ans--; if(pre)putchar(‘ ‘); printf("%d",i); //Out(i); pre=i; } } putchar(‘\n‘); } return 0; }
2. Pinball Game 3D(hdu4742)
题目意思:给你三个序列X,Y,Z求最长上升序列的长度。同时输出可以构成最长上升序列的方法数。
和之前的一题几乎一样,多了一个维护方法数。所以树状数组不仅要维护一个Dp的值,同时还要维护一个方法数。如果Dp更新了,方法数就是更新它的值,反之,在原来的基础上要加上那种方法的个数。
代码:
#include <iostream> #include <sstream> #include <ios> #include <iomanip> #include <functional> #include <algorithm> #include <vector> #include <string> #include <list> #include <queue> #include <deque> #include <stack> #include <set> #include <map> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <climits> #include <bitset> #include <cctype> #define NINF -0x3f3f3f3f #define MP(X,Y) make_pair(X,Y) #define PB(X) push_back(X) #define REP(X,N) for(int X=0;X<N;X++) #define REP2(X,L,R) for(int X=L;X<=R;X++) #define DEP(X,R,L) for(int X=R;X>=L;X--) #define CLR(A,X) memset(A,X,sizeof(A)) #define IT iterator #define M_PI 3.14159265358979323846 #define _ ios_base::sync_with_stdio(0);cin.tie(0); #define X first #define Y second #define MAX_V 10101 #define maxn 100009 #define lowbit(X) (X & (-X)) #include<ctime> using namespace std; typedef long long ll; typedef pair<int,ll>PII; typedef pair<PII,int>PPI; int Fu[maxn*3]; ll Num[maxn]; int D[maxn]; const ll MOD=(1<<30); struct node{ int x,y,z; bool operator <(const node&B) const{ if(x!=B.x) return x<B.x; if(y!=B.y) return y<B.y; return z<B.z; } }A[maxn]; struct node2{ int x,y,z,id; bool operator<(const node2&B)const{ if(y!=B.y) return y<B.y; if(z!=B.z) return z<B.z; return id<B.id; } }q1[maxn],q2[maxn]; PII dat[maxn*3]; void update(int k,int val,ll num){ while(k<maxn*3){ if(val>dat[k].X){ dat[k].X=val; dat[k].Y=num; } else if(val==dat[k].X){ dat[k].Y+=num; dat[k].Y%=MOD; } k+=lowbit(k); } } PII query(int k){ PII Max=MP(0,0); while(k>0){ if(dat[k].X>Max.X){ Max=dat[k]; } else if(dat[k].X==Max.X){ Max.Y+=dat[k].Y; Max.Y%=MOD; } k-=lowbit(k); } return Max; } void clear(int k){ while(k<maxn*3){ dat[k]=MP(0,0); k+=lowbit(k); } } void cdq(int l,int r){ if(l==r)return ; int mid=l+r>>1; cdq(l,mid); for(int i=l;i<=mid;++i){ q1[i].x=A[i].x;q1[i].y=A[i].y; q1[i].z=A[i].z;q1[i].id=i; } for(int i=mid+1;i<=r;++i){ q2[i].x=A[i].x;q2[i].y=A[i].y; q2[i].z=A[i].z;q2[i].id=i; } sort(q1+l,q1+mid+1); sort(q2+mid+1,q2+r+1); for(int i=mid+1,j=l;i<=r;++i){ while(j<=mid && q1[j].y<=q2[i].y){ update(q1[j].z,D[q1[j].id],Num[q1[j].id]); j++; } PII temp=query(q2[i].z); if(D[q2[i].id]<temp.X+1){ D[q2[i].id]=temp.X+1; Num[q2[i].id]=temp.Y; } else if(D[q2[i].id]==temp.X+1){ Num[q2[i].id]+=temp.Y; Num[q2[i].id]%=MOD; } } for(int i=l;i<=mid;++i){ clear(q1[i].z); } cdq(mid+1,r); } int Scan(){ int res=0, ch; ch=getchar(); //windows»Ø³µ\r\n linux»Ø³µ\n ·ÀÖ¹Êý¾ÝÖÐûÓÐÈ¥³ý\r if(ch==10) ch=getchar(); if(ch>=‘0‘&&ch<=‘9‘) res=ch-‘0‘; else return -1; while((ch=getchar())>=‘0‘&&ch<=‘9‘) res=res*10+ch-‘0‘; return res; } void Out(ll a){ if(a>9) Out(a/10); putchar(a%10+‘0‘); } int main() { int T; //scanf("%d",&T); T=Scan(); while(T--){ int n; //scanf("%d",&n); n=Scan(); int tot=0; for(int i=1;i<=n;++i){ //scanf("%d%d%d",&A[i].x,&A[i].y,&A[i].z); A[i].x=Scan();A[i].y=Scan();A[i].z=Scan(); Fu[tot++]=A[i].x; Fu[tot++]=A[i].y; Fu[tot++]=A[i].z; } sort(Fu,Fu+tot); for(int i=1;i<=n;++i){ D[i]=Num[i]=1; A[i].x=lower_bound(Fu,Fu+tot,A[i].x)-Fu+1; A[i].y=lower_bound(Fu,Fu+tot,A[i].y)-Fu+1; A[i].z=lower_bound(Fu,Fu+tot,A[i].z)-Fu+1; } sort(A+1,A+n+1); cdq(1,n); int Max=0; for(int i=1;i<=n;++i){ Max=max(Max,D[i]); } ll sum=0; for(int i=1;i<=n;++i){ if(D[i]==Max){ sum=sum+Num[i]; sum%=MOD; } } printf("%d %I64d\n",Max,sum); } return 0; }
3. Machine Works(hdu3842)
这是2011WorldFinal的一道题目,所以做的人不多。要是我当时看到是WorldFinal的题,肯定吓得直接跳过了。但是事实上难度还可以。
题目意思:
初始时,给你一笔资金C元,以及一段时间D天。在其中的某天会提供给你购买机器的机会,你可以选择购买或者不购买。买机器需要花费P元,卖出可以获得R元,从够买机器的第二天起,到卖出机器的前一天,你可以每天获得G元。最后一天将卖出所有机器。同时,由于场地限制,最多只能同时拥有一台机器。
首先我们可以用D[i]来维护前一个状态结束,刚刚到达i这个机器时,所能拥有的最大值。
D[i]=max{D[j]-P[j]+R[j]+G[j]*(day[i]-day[j]-1)}
因为j的信息都是已知的,所以可以化解为
D[i]=C+k*day[i];
所以也就化解为斜率优化DP的问题了。
所以我们接下来按照斜率从小到大来排序,用一个队列来维护。队列中的元素满足k是递增的。所以队尾的元素一定在某些x时候会比队尾的前一个元素大。现在当你要新加入一个元素到末尾的时候,新加入的元素可能无论在什么时候都比原先在队尾的元素大,这样就删掉那个元素。
同时对于队首的维护,当目前的x不是最大时,删除这个队首的值。DP一下就可以了。
代码:
#include <iostream> #include <sstream> #include <ios> #include <iomanip> #include <functional> #include <algorithm> #include <vector> #include <string> #include <list> #include <queue> #include <deque> #include <stack> #include <set> #include <map> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <climits> #include <bitset> #include <cctype> #define NINF -0x3f3f3f3f #define MP(X,Y) make_pair(X,Y) #define PB(X) push_back(X) #define REP(X,N) for(int X=0;X<N;X++) #define REP2(X,L,R) for(int X=L;X<=R;X++) #define DEP(X,R,L) for(int X=R;X>=L;X--) #define CLR(A,X) memset(A,X,sizeof(A)) #define IT iterator #define M_PI 3.14159265358979323846 #define _ ios_base::sync_with_stdio(0);cin.tie(0); #define X first #define Y second #define MAX_V 10101 #define maxn 123456 #define lowbit(X) (X & (-X)) #include<ctime> using namespace std; typedef long long ll; typedef pair<int,int>PII; typedef pair<PII,int>PPI; //19:52 ll D[maxn]; struct node{ int day,pri,ret,val,id; bool operator <(const node &B)const { return day<B.day; } }A[maxn]; struct node2{ int day,pri,ret,val,id; bool operator <(const node2 &B)const { if(val==B.val){ ll t1=D[id]+ret-pri-1ll*(day+1)*val; ll t2=D[B.id]+B.ret-B.pri-1ll*(B.day+1)*B.val; return t1<t2; } return val<B.val; } }q1[maxn],q2[maxn]; ll K[maxn],B[maxn]; ll f(int i,int x){ ll ans=K[i]*x+B[i]; return ans; } bool check(int i1,int i2,ll k3,ll b3){ ll k1=K[i1],b1=B[i1],k2=K[i2],b2=B[i2]; return 1.0*(k3-k2)*(b1-b3)-1.0*(b2-b3)*(k3-k1)<0; //return (double)(k2-k1)*(b3-b1)-(double)(k3-k1)*(b2-b1)<0; } void cdq(int l,int r){ if(l==r)return ; int mid=l+r>>1; cdq(l,mid); for(int i=l;i<=mid;++i){ q1[i].day=A[i].day;q1[i].pri=A[i].pri; q1[i].ret=A[i].ret;q1[i].val=A[i].val; q1[i].id=A[i].id; } for(int i=mid+1;i<=r;++i){ q2[i].day=A[i].day;q2[i].pri=A[i].pri; q2[i].ret=A[i].ret;q2[i].val=A[i].val; q2[i].id=A[i].id; } sort(q1+l,q1+mid+1); int head=0,tail=-1; for(int i=l;i<=mid;++i){ if(D[q1[i].id]<q1[i].pri)continue; ll t1=q1[i].val; ll t2=D[q1[i].id]+q1[i].ret-q1[i].pri-1ll*(q1[i].day+1)*q1[i].val; // if(tail-head+1<2){ // K[++tail]=t1; // B[tail]=t2; // //if(head-tail==1 && K[head]==K[tail] && B[head]<=B[tail])head++; // } // else{ while(tail>head && !check(tail-1,tail,t1,t2))tail--; K[++tail]=t1; B[tail]=t2; //} } for(int i=mid+1;i<=r;++i){ int d=q2[i].day; while(head<tail && f(head,d)<=f(head+1,d))head++; ll temp=f(head,d); if(temp<0)continue; D[q2[i].id]=max(temp,D[q2[i].id]); } cdq(mid+1,r); } int main() { int n,c,d; int tt=0; while(~scanf("%d%d%d",&n,&c,&d)){ if(n==0 && c==0 && d==0)break; D[0]=c; A[0].id=0;A[0].day=0;A[0].val=0;A[0].pri=0;A[0].ret=0; for(int i=1;i<=n;++i){ scanf("%d%d%d%d",&A[i].day,&A[i].pri,&A[i].ret,&A[i].val); A[i].id=i; D[i]=c; } A[n+1].day=d+1;A[n+1].id=n+1; A[n+1].pri=A[n+1].val=A[n+1].ret=0; D[n+1]=c; sort(A+1,A+n+1); cdq(0,n+1); printf("Case %d: %I64d\n",++tt,D[n+1]); } return 0; }
4. 动态逆序对(BZOJ3295)
题目意思:给你一个序列求每次删除一个元素之前的逆序对的个数。
假设你要删除一个元素,那么减少的逆序对的个数不仅和他前面位置的有关,还和他后面位置的数有关。所以假设已经知道在最初状态时在他前面和后面分别的逆序对数。在删除的时候,更新这两个值,问题就解决了。
至于逆序对的个数,预处理一下就可以了。
代码:
#include <iostream> #include <sstream> #include <ios> #include <iomanip> #include <functional> #include <algorithm> #include <vector> #include <string> #include <list> #include <queue> #include <deque> #include <stack> #include <set> #include <map> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <climits> #include <bitset> #include <cctype> #define NINF -0x3f3f3f3f #define MP(X,Y) make_pair(X,Y) #define PB(X) push_back(X) #define REP(X,N) for(int X=0;X<N;X++) #define REP2(X,L,R) for(int X=L;X<=R;X++) #define DEP(X,R,L) for(int X=R;X>=L;X--) #define CLR(A,X) memset(A,X,sizeof(A)) #define IT iterator #define M_PI 3.14159265358979323846 #define _ ios_base::sync_with_stdio(0);cin.tie(0); #define X first #define Y second #define MAX_V 10101 #define maxn 123456 #define lowbit(X) (X & (-X)) #include<ctime> using namespace std; typedef long long ll; typedef pair<int,int>PII; typedef pair<PII,int>PPI; ll dat[maxn]; void update(int k,int val){ while(k<maxn){ dat[k]+=val; k+=lowbit(k); } } ll query(int k){ ll sum=0; while(k>0){ sum+=dat[k]; k-=lowbit(k); } return sum; } int A[maxn]; int Pos[maxn]; ll L[maxn],R[maxn]; struct node{ int val,id; bool operator <(const node &B)const { return Pos[val]<Pos[B.val]; } }Q[maxn],q1[maxn],q2[maxn]; ll Ans[maxn]; int n,m; void solve(int l,int r){ if(l==r){ // cout<<Pos[Q[l].val]<<" "<<L[Pos[Q[l].val]]<<" "<<R[Pos[Q[l].val]]<<endl; Ans[l]=L[Pos[Q[l].val]]+R[Pos[Q[l].val]]; return ; } int mid=l+r>>1; solve(l,mid); int l1=0,l2=0; for(int i=l;i<=mid;++i){ q1[l1++]=Q[i]; } for(int i=mid+1;i<=r;++i){ q2[l2++]=Q[i]; } sort(q1,q1+l1); sort(q2,q2+l2); int j=0; for(int i=0;i<l2;++i){ while(j<l1 && Pos[q1[j].val]<Pos[q2[i].val]){ update(q1[j].val,1); //cout<<" "<<Pos[q1[j].val]<<" "<<n<<" "<<query(n)<<endl; j++; } int ps=Pos[q2[i].val]; L[ps]-=query(n)-query(q2[i].val); } for(int i=0;i<j;++i){ update(q1[i].val,-1); } //---- j=l1-1; for(int i=l2-1;i>=0;--i){ while(j>=0 && Pos[q1[j].val]>Pos[q2[i].val]){ update(q1[j].val,1); j--; } int ps=Pos[q2[i].val]; R[ps]-=query(q2[i].val); } for(int i=l1-1;i>j;--i){ update(q1[i].val,-1); } solve(mid+1,r); } int main() { while(~scanf("%d%d",&n,&m)){ memset(dat,0,sizeof(dat)); ll sum=0; for(int i=1;i<=n;++i){ scanf("%d",&A[i]); L[i]=query(n)-query(A[i]); sum+=L[i]; update(A[i],1); Pos[A[i]]=i; } memset(dat,0,sizeof(dat)); for(int i=n;i>=1;--i){ R[i]=query(A[i]); update(A[i],1); } int a; for(int i=1;i<=m;++i){ scanf("%d",&a); Q[i].val=a; Q[i].id=i; } memset(dat,0,sizeof(dat)); solve(1,m); for(int i=1;i<=m;++i){ printf("%lld\n",sum); sum-=Ans[i]; } } return 0; }
5. Crowd(hdu4456)
题目意思:给你一张图,有两种查询,1.是给某个点的值增加Z 2.是查询曼哈顿距离小于某个点的值的和。
首先,需要的是坐标轴旋转。然后就可以用CDQ分治,或者是二维树状数组来搞了。
这篇讲的很好,我就不废话了。
http://www.aiuxian.com/article/p-2286542.html
代码:树状数组
#include <iostream> #include <sstream> #include <ios> #include <iomanip> #include <functional> #include <algorithm> #include <vector> #include <string> #include <list> #include <queue> #include <deque> #include <stack> #include <set> #include <map> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <climits> #include <bitset> #include <cctype> #define NINF -0x3f3f3f3f #define MP(X,Y) make_pair(X,Y) #define PB(X) push_back(X) #define REP(X,N) for(int X=0;X<N;X++) #define REP2(X,L,R) for(int X=L;X<=R;X++) #define DEP(X,R,L) for(int X=R;X>=L;X--) #define CLR(A,X) memset(A,X,sizeof(A)) #define IT iterator #define M_PI 3.14159265358979323846 #define _ ios_base::sync_with_stdio(0);cin.tie(0); #define X first #define Y second #define MAX_V 10101 #define maxn 1 #define lowbit(X) (X & (-X)) #include<ctime> using namespace std; typedef long long ll; typedef pair<int,int>PII; typedef pair<PII,int>PPI; void change(int &x,int &y,int n){ int t=x-y+n; y=x+y-1; x=t; } const int MOD=3001003; int dat[MOD]; int num[MOD]; int Hash(int x){ int t=x%MOD; while(1){ if(num[t]==x || num[t]==-1){ num[t]=x; return t; } t++; if(t==MOD)t=0; } return -1; } int gethash(int x){ int t=x%MOD; while(1){ if(num[t]==-1 || num[t]==x)return t; t++; if(t==MOD)t=0; } return -1; } int Bound; void add(int x,int y,int add){ for(int i=x;i<Bound;i+=lowbit(i)){ for(int j=y;j<Bound;j+=lowbit(j)){ dat[Hash(i*Bound+j)]+=add; } } } int getsum(int x,int y){ int ans=0; for(int i=x;i>0;i-=lowbit(i)){ for(int j=y;j>0;j-=lowbit(j)){ ans+=dat[gethash(i*Bound+j)]; } } return ans; } int query(int a,int b,int c,int d){ int ans=0; a=max(a,1);b=max(b,1); c=min(Bound-1,c); d=min(Bound-1,d); ans=getsum(c,d)+getsum(a-1,b-1) -getsum(a-1,d)-getsum(c,b-1); return ans; } int main() { int n,m; while(1){ scanf("%d",&n); if(n==0)break; scanf("%d",&m); Bound=n*2+1; memset(dat,0,sizeof(dat)); memset(num,-1,sizeof(num)); int a,x,y,z; for(int i=0;i<m;++i){ scanf("%d%d%d%d",&a,&x,&y,&z); change(x,y,n); if(a==1){ add(x,y,z); } else{ printf("%d\n",query(x-z,y-z,x+z,y+z)); } } } return 0; }
代码:CDQ
#include <iostream> #include <sstream> #include <ios> #include <iomanip> #include <functional> #include <algorithm> #include <vector> #include <string> #include <list> #include <queue> #include <deque> #include <stack> #include <set> #include <map> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <climits> #include <bitset> #include <cctype> #define NINF -0x3f3f3f3f #define MP(X,Y) make_pair(X,Y) #define PB(X) push_back(X) #define REP(X,N) for(int X=0;X<N;X++) #define REP2(X,L,R) for(int X=L;X<=R;X++) #define DEP(X,R,L) for(int X=R;X>=L;X--) #define CLR(A,X) memset(A,X,sizeof(A)) #define IT iterator #define M_PI 3.14159265358979323846 #define _ ios_base::sync_with_stdio(0);cin.tie(0); #define X first #define Y second #define MAX_V 9 #define maxn 81234 #define lowbit(X) (X & (-X)) #include<ctime> using namespace std; typedef long long ll; typedef pair<int,int>PII; typedef pair<PII,int>PPI; void change(int &x,int &y,int n){ int t=x-y+n; y=x+y-1; x=t; } int Bound; struct node{ int s,x,y,z,id; bool operator <(const node &B)const{ if(x!=B.x)return x<B.x; return y<B.y; } }Q[maxn*4],q1[maxn*4],q2[maxn*4]; int Ans[maxn]; void addnode(int s,int x,int y,int z,int id,int &tot){ Q[++tot].s=s; Q[tot].x=x;Q[tot].y=y;Q[tot].z=z; Q[tot].id=id; } int dat[maxn]; void add(int k,int val){ while(k<Bound){ dat[k]+=val; k+=lowbit(k); } } int query(int k){ int sum=0; while(k>0){ sum+=dat[k]; k-=lowbit(k); } return sum; } void cdq(int l,int r){ if(l==r)return; int mid=l+r>>1; cdq(l,mid); int l1=0,l2=0; for(int i=l;i<=mid;++i){ q1[l1++]=Q[i]; } for(int i=mid+1;i<=r;++i){ q2[l2++]=Q[i]; } sort(q1,q1+l1); sort(q2,q2+l2); int j=0; for(int i=0;i<l2;++i){ if(q2[i].s==1)continue; while(j<l1 &&(q1[j].s==2 || q1[j].x<=q2[i].x)){ if(q1[j].s==1)add(q1[j].y,q1[j].z); j++; } Ans[q2[i].id]+=query(q2[i].y)*q2[i].z; } for(int i=j-1;i>=0;--i) if(q1[i].s==1)add(q1[i].y,-q1[i].z); cdq(mid+1,r); } int main() { int n,m; while(1){ scanf("%d",&n); if(n==0)break; scanf("%d",&m); Bound=n*2+1; int s,x,y,z; int a,b,c,d; int tot=0; for(int i=1;i<=m;++i){ scanf("%d%d%d%d",&s,&x,&y,&z); change(x,y,n); if(s==1){ addnode(s,x,y,z,i,tot); } else{ a=x-z;b=y-z;c=x+z;d=y+z; a=max(a,1);b=max(b,1); c=min(Bound-1,c); d=min(Bound-1,d); addnode(s,c,d,1,i,tot); addnode(s,a-1,b-1,1,i,tot); addnode(s,c,b-1,-1,i,tot); addnode(s,a-1,d,-1,i,tot); } } memset(Ans,0,sizeof(Ans)); cdq(1,tot); for(int i=1;i<=tot;++i){ if(Q[i].s==2){ printf("%d\n",Ans[Q[i].id]); i+=3; } } } return 0; }
6.隐藏题目
这是一道高神出的题目。因为这题目前还未出现过,所以这部分题解不公开。
/*
(------这是一个坑------)
*/
标签:
原文地址:http://www.cnblogs.com/baobaopangzi88/p/4820004.html