初识CDQ分治
[BZOJ 1176:单点修改,查询子矩阵和]:
1176: [Balkan2007]Mokia
Time Limit: 30 Sec Memory Limit: 162 MBSubmit: 2005 Solved: 894
[Submit][Status][Discuss]
Description
维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000.
Input
第一行两个整数,S,W;其中S为矩阵初始值;W为矩阵大小
接下来每行为一下三种输入之一(不包含引号):
"1 x y a"
"2 x1 y1 x2 y2"
"3"
输入1:你需要把(x,y)(第x行第y列)的格子权值增加a
输入2:你需要求出以左下角为(x1,y1),右上角为(x2,y2)的矩阵内所有格子的权值和,并输出
输入3:表示输入结束
Output
对于每个输入2,输出一行,即输入2的答案
Sample Input
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3
Sample Output
5
HINT
保证答案不会超过int范围
Source
分析:
第一道CDQ分治的题目TAT...
查询的限制——序
对于一个数据结构题而言(或者需要运用数据结构的地方),我们无非就是做两件操作,一是修改,二是查询
对于修改而言,有插入,删除,变更(其实等价于删除再插入)这几种方式
那么查询的本质是什么呢
我们思考所遇到过的数据结构题,可以发现查询实际上就在做一件事情:
把符合本次查询的限制的修改对答案产生的效果合并起来
满足这种限制通常表现为一种序的要求,并且这种序是广义的,符合限制的操作往往是按某种序(或多种序)排序后的操作的前缀
通常来说,查询一定有时间上的限制,也就是要求考虑发生在某个时刻之前的所有查询,对于一个问题而言,假如所有查询要求的发生时刻相同,那这就是一个静态查询问题,如果要求发生的时刻随着查询而变,那这就是一个动态修改问题,动态修改问题较静态查询而言复杂很多,往往需要高级数据结构,可持久化等手段,而静态查询简单很多,例如时间倒流,twopointers之类的方法都是很好的选择
我理解的CDQ分治就是通过离线去掉时间的限制(或者某一维的限制),把动态修改问题转化为静态查询问题...
对于这道题来说,我们可以把查询操作分为4个询问前缀和的操作...
我们把所有的操作按照x排序,那么我们查询的时候维护y的树状数组就可以...这样达到了降维的目的...
我们把操作按照x排序,然后对于当前操作区间[l,r],可以应用分治的思想,把区间按照时间为关键字分为[l,mid]和[mid+1,r]两个区间(在两个区间内x依然有序),对于区间内部的修改操作对查询操作的影响,这是一个相同的子问题,所以我们递归解决即可...我们只需要处理的就是[l,mid]中的修改操作对[mid+1,r]中的查询操作的影响...这个树状数组处理即可...
代码:
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 //by NeighThorn 6 using namespace std; 7 //大鹏一日同风起,扶摇直上九万里 8 9 const int maxn=2000000+5,maxm=200000+5; 10 11 struct M{ 12 int x,y,val,pos,ans; 13 M() {} 14 M(int a,int b,int c,int d); 15 }q[maxm],q2[maxm]; 16 17 int n,m,op,T,tr[maxn],tim[maxn]; 18 19 M :: M(int a=0,int b=0,int c=0,int d=0){ 20 x=a,y=b,val=c,pos=d,ans=0; 21 } 22 23 inline void insert(int x,int y){ 24 for(;x<=n;x+=x&(-x)){ 25 if(tim[x]!=T) 26 tr[x]=0; 27 tim[x]=T,tr[x]+=y; 28 } 29 } 30 31 inline int query(int x){ 32 int res=0; 33 for(;x;x-=x&(-x)) 34 if(tim[x]==T) 35 res+=tr[x]; 36 return res; 37 } 38 39 inline bool cmp1(M a,M b){ 40 return a.x<b.x; 41 } 42 43 inline bool cmp2(M a,M b){ 44 return a.pos<b.pos; 45 } 46 47 inline void CDQ(int l,int r){ 48 if(l==r) 49 return; 50 int mid=(l+r)>>1,l1=l,l2=mid+1; 51 for(int i=l;i<=r;i++){ 52 if(q[i].pos<=mid) 53 q2[l1++]=q[i]; 54 else 55 q2[l2++]=q[i]; 56 } 57 memcpy(q+l,q2+l,sizeof(q[0])*(r-l+1));CDQ(l,mid); 58 int j=l;T++; 59 for(int i=mid+1;i<=r;i++){ 60 for(;q[j].x<=q[i].x&&j<=mid;j++) 61 if(q[j].val!=20010106) 62 insert(q[j].y,q[j].val); 63 if(q[i].val==20010106) 64 q[i].ans+=query(q[i].y); 65 } 66 CDQ(mid+1,r); 67 l1=l,l2=mid+1; 68 for(int i=l;i<=r;i++){ 69 if((q[l1].x<q[l2].x&&l1<=mid)||l2>r) 70 q2[i]=q[l1++]; 71 else 72 q2[i]=q[l2++]; 73 } 74 memcpy(q+l,q2+l,sizeof(q[0])*(r-l+1)); 75 } 76 77 signed main(void){ 78 int l,s,x,y;m=0; 79 scanf("%d%d",&n,&n); 80 while(scanf("%d",&op)&&op!=3){ 81 if(op==1) 82 scanf("%d%d%d",&x,&y,&s),q[++m]=M(x,y,s,m); 83 else 84 scanf("%d%d%d%d",&l,&s,&x,&y), 85 q[++m]=M(l-1,s-1,20010106,m), 86 q[++m]=M(l-1,y ,20010106,m), 87 q[++m]=M(x ,s-1,20010106,m), 88 q[++m]=M(x ,y ,20010106,m); 89 } 90 sort(q+1,q+m+1,cmp1);CDQ(1,m);sort(q+1,q+m+1,cmp2); 91 for(int i=1,lala;i<=m;i++) 92 if(q[i].val==20010106){ 93 lala =q[i++].ans; 94 lala-=q[i++].ans; 95 lala-=q[i++].ans; 96 lala+=q[i ].ans; 97 printf("%d\n",lala); 98 } 99 return 0; 100 }//Cap ou pas cap. Cap.
[BZOJ 3163:背包问题]:
3163: [Heoi2013]Eden的新背包问题
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 382 Solved: 252
[Submit][Status][Discuss]
Description
“寄没有地址的信,这样的情绪有种距离,你放着谁的歌曲,是怎样的心心静,能不能说给我听。”
失忆的Eden总想努力地回忆起过去,然而总是只能清晰地记得那种思念的感觉,却不能回忆起她的音容笑貌。 记忆中,她总是喜欢给Eden出谜题:在 valentine’s day 的夜晚,两人在闹市中闲逛时,望着礼品店里精巧玲珑的各式玩偶,她突发奇想,问了 Eden这样的一个问题:有n个玩偶,每个玩偶有对应的价值、价钱,每个玩偶都可以被买有限次,在携带的价钱m固定的情况下,如何选择买哪些玩偶以及每个玩偶买多少个,才能使得选择的玩偶总价钱不超过m,且价值和最大。众所周知的,这是一个很经典的多重背包问题,Eden很快解决了,不过她似乎因为自己的问题被飞快解决感到了一丝不高兴,于是她希望把问题加难:多次 询问,每次询问都将给出新的总价钱,并且会去掉某个玩偶(即这个玩偶不能被选择),再问此时的多重背包的答案(即前一段所叙述的问题)。
这下Eden 犯难了,不过Eden不希望自己被难住,你能帮帮他么?
Input
第一行一个数n,表示有n个玩偶,玩偶从0开始编号
第二行开始后面的 n行,每行三个数 ai, bi, c i,分别表示买一个第i个玩偶需
要的价钱,获得的价值以及第i个玩偶的限购次数。
接下来的一行为q,表示询问次数。
接下来q行,每行两个数di. ei表示每个询问去掉的是哪个玩偶(注意玩偶从0开始编号)以及该询问对应的新的总价钱数。(去掉操作不保留,即不同询问互相独立)
Output
输出q行,第i行输出对于第 i个询问的答案。
Sample Input
2 3 4
1 2 1
4 1 2
2 1 1
3 2 3
5
1 10
2 7
3 4
4 8
0 5
Sample Output
11
6
12
4
HINT
一共五种玩偶,分别的价钱价值和限购次数为 (2,3,4), (1,2,1), (4,1,2), (2,1,1),(3,2,3)。五个询问,以第一个询问为例。第一个询问表示的是去掉编号为1的玩偶,且拥有的钱数为10时可以获得的最大价值,则此时剩余玩偶为(2,3,4),(4,1,2),(2,1,1),(3,2,3),若把编号为0的玩偶买4个(即全买了),然后编号为3的玩偶买一个,则刚好把10元全部花完,且总价值为13。可以证明没有更优的方案了。注意买某种玩偶不一定要买光。
100. 数据满足1 ≤ n ≤ 1000, 1 ≤ q ≤ 3*105 , 1 ≤ a
i、bi、c i ≤ 100, 0 ≤ d i < n, 0 ≤ei ≤ 1000。
Source
分析:
对于删除物品分治...
我们定义CDQ(l,r)为删除[l,r]这个区间的物品的dp值,那么递归到[l,l]的时候就可以更新答案了...
怎么计算...因为[l,mid]的dp值一定包含了[mid+1,r]之间的物品,所以我们可以用[mid+1,r]之间的物品更新dp值之后传入[l,mid]进行计算,同理,用[l,mid]的物品更新dp值然后传入[mid+1,r]进行计算...
其实为了保证复杂度要用单调队列优化背包...但是这题数据水...我很懒...
代码:
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 //by NeighThorn 6 using namespace std; 7 //大鹏一日同风起,扶摇直上九万里 8 9 const int maxn=1000+5,maxq=300000+5; 10 11 int n,m,cnt,v[maxq],co[maxn],hd[maxn],ans[maxq],pos[maxq],nxt[maxq],val[maxn],num[maxn],f[15][maxn]; 12 13 inline void add(int s,int x,int y){ 14 pos[cnt]=y;v[cnt]=s;nxt[cnt]=hd[x];hd[x]=cnt++; 15 } 16 17 inline void dp(int *f,int x){ 18 for(int i=1;i<=num[x];i++) 19 for(int j=maxn-5;j>=co[x];j--) 20 f[j]=max(f[j],f[j-co[x]]+val[x]); 21 } 22 23 inline void CDQ(int l,int r,int d){ 24 if(l==r){ 25 for(int i=hd[l];i!=-1;i=nxt[i]) 26 ans[pos[i]]=f[d-1][v[i]]; 27 return; 28 } 29 int mid=(l+r)>>1; 30 for(int i=0;i<maxn;i++) 31 f[d][i]=f[d-1][i]; 32 for(int i=mid+1;i<=r;i++) 33 dp(f[d],i); 34 CDQ(l,mid,d+1); 35 for(int i=0;i<maxn;i++) 36 f[d][i]=f[d-1][i]; 37 for(int i=l;i<=mid;i++) 38 dp(f[d],i); 39 CDQ(mid+1,r,d+1); 40 } 41 42 signed main(void){ 43 scanf("%d",&n);cnt=0; 44 memset(hd,-1,sizeof(hd)); 45 for(int i=1;i<=n;i++) 46 scanf("%d%d%d",&co[i],&val[i],&num[i]); 47 scanf("%d",&m); 48 for(int i=1,x,y;i<=m;i++) 49 scanf("%d%d",&x,&y),add(y,x+1,i); 50 CDQ(1,n,1); 51 for(int i=1;i<=m;i++) 52 printf("%d\n",ans[i]); 53 return 0; 54 }//Cap ou pas cap. Cap.
[BZOJ 3237: 无向图是否联通]:
3237: [Ahoi2013]连通图
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1155 Solved: 396
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
1 2
2 3
3 4
4 1
2 4
3
1 5
2 2 3
2 1 2
Sample Output
Disconnected
Connected
HINT
N<=100000 M<=200000 K<=100000
Source
分析:
一开始想的是CDQ(l,r)代表的是删掉[l,r]这一整段区间的边无向图的状态,但是发现酱紫是不行滴...
然后我们发现我们可以对query进行分治,CDQ(l,r)代表的是去掉[l,r]这段询问的边无向图的状态...我们用并查集来维护...
学习到一个新的方法...用栈来维护并查集的压缩过程,这样可以回溯...
因为并查集没有按秩合并...所以被YSQ吐槽为“叽里咕噜滚雪球式并查集”...( ̄_ ̄|||)...
好吧...按秩合并确实快...4s和12s的差距TAT...
代码:
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 //by NeighThorn 6 using namespace std; 7 //大鹏一日同风起,扶摇直上九万里 8 9 const int maxn=100000+5,maxm=200000+5; 10 11 int n,m,Q,T,tail,fa[maxn],ans[maxn],stk[maxn*300],vis[maxm]; 12 13 struct M{ 14 int num,id[7]; 15 }q[maxn]; 16 17 struct G{ 18 int x,y; 19 }e[maxm]; 20 21 inline int find(int x){ 22 if(fa[x]==x) 23 return x; 24 stk[++tail]=x;stk[++tail]=fa[x]; 25 return fa[x]=find(fa[x]); 26 } 27 28 inline void merge(int x,int y){ 29 int fx=find(x),fy=find(y); 30 if(fx!=fy) 31 stk[++tail]=fx,stk[++tail]=fx,fa[fx]=fy; 32 } 33 34 inline void CDQ(int l,int r){ 35 int mid=(l+r)>>1,lala=tail; 36 if(l==r){ 37 for(int i=1;i<=q[l].num&&ans[l];i++) 38 if(find(e[q[l].id[i]].x)!=find(e[q[l].id[i]].y)) 39 ans[l]=0; 40 while(lala!=tail) 41 fa[stk[tail-1]]=stk[tail],tail-=2; 42 return; 43 }T++; 44 for(int i=l;i<=mid;i++) 45 for(int j=1;j<=q[i].num;j++) 46 vis[q[i].id[j]]=T; 47 for(int i=mid+1;i<=r;i++) 48 for(int j=1;j<=q[i].num;j++) 49 if(vis[q[i].id[j]]!=T) 50 merge(e[q[i].id[j]].x,e[q[i].id[j]].y); 51 CDQ(l,mid);T++; 52 while(lala!=tail) 53 fa[stk[tail-1]]=stk[tail],tail-=2; 54 for(int i=mid+1;i<=r;i++) 55 for(int j=1;j<=q[i].num;j++) 56 vis[q[i].id[j]]=T; 57 for(int i=l;i<=mid;i++) 58 for(int j=1;j<=q[i].num;j++) 59 if(vis[q[i].id[j]]!=T) 60 merge(e[q[i].id[j]].x,e[q[i].id[j]].y); 61 CDQ(mid+1,r); 62 while(lala!=tail) 63 fa[stk[tail-1]]=stk[tail],tail-=2; 64 } 65 66 signed main(void){ 67 scanf("%d%d",&n,&m); 68 memset(vis,-1,sizeof(vis)); 69 for(int i=1;i<=m;i++) 70 scanf("%d%d",&e[i].x,&e[i].y); 71 scanf("%d",&Q); 72 for(int i=1;i<=Q;i++){ 73 scanf("%d",&q[i].num);ans[i]=1; 74 for(int j=1;j<=q[i].num;j++) 75 scanf("%d",&q[i].id[j]),vis[q[i].id[j]]=0; 76 } 77 for(int i=1;i<=n;i++) 78 fa[i]=i; 79 for(int i=1;i<=m;i++) 80 if(vis[i]==-1) 81 merge(e[i].x,e[i].y); 82 CDQ(1,Q); 83 for(int i=1;i<=Q;i++) 84 ans[i]?puts("Connected"):puts("Disconnected"); 85 return 0; 86 }//Cap ou pas cap. Cap.
[BZOJ 2001:动态最小生成树]:
2001: [Hnoi2010]City 城市建设
Time Limit: 20 Sec Memory Limit: 162 MBSubmit: 1129 Solved: 552
[Submit][Status][Discuss]
Description
PS国是一个拥有诸多城市的大国,国王Louis为城市的交通建设可谓绞尽脑汁。Louis可以在某些城市之间修建道路,在不同的城市之间修建道路需要不同的花费。Louis希望建造最少的道路使得国内所有的城市连通。但是由于某些因素,城市之间修建道路需要的花费会随着时间而改变,Louis会不断得到某道路的修建代价改变的消息,他希望每得到一条消息后能立即知道使城市连通的最小花费总和, Louis决定求助于你来完成这个任务。
Input
文件第一行包含三个整数N,M,Q,分别表示城市的数目,可以修建的道路个数,及收到的消息个数。 接下来M行,第i+1行有三个用空格隔开的整数Xi,Yi,Zi(1≤Xi,Yi≤n, 0≤Zi≤50000000),表示在城市Xi与城市Yi之间修建道路的代价为Zi。接下来Q行,每行包含两个数k,d,表示输入的第k个道路的修建代价修改为d(即将Zk修改为d)。
Output
输出包含Q行,第i行输出得知前i条消息后使城市连通的最小花费总和。
Sample Input
1 2 1
2 3 2
3 4 3
4 5 4
5 1 5
1 6
1 1
5 3
Sample Output
10
9
HINT
【数据规模】 对于20%的数据, n≤1000,m≤6000,Q≤6000。 有20%的数据,n≤1000,m≤50000,Q≤8000,修改后的代价不会比之前的代价低。 对于100%的数据, n≤20000,m≤50000,Q≤50000。
Source
分析:
这道题的思想主要在于通过分治来缩小图的规模,从而降低复杂度...
我们定义CDQ(l,r)为处理[l,r]的询问...那么这个时候[1,l-1]的边已经固定了...
有一下两个操作:
No.1 合并必须边:
我们把[l,r]的边设为-inf,跑最小生成树,树边就是必须边...(原因自己YY)...
No.2 删除多余边:
我们把[l,r]的边设为+inf,跑最小生成树,非树边是多余边...
然后递归边界为[l,l]这个时候跑最小生成树然后加上合并的权值就是当前的ans...
讲道理...这题主要是细节...我调了好久...要了数据才过...(好吧其实数据没有什么用...看样例就100了...这没法调啊w(゚Д゚)w)
代码:
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 //by NeighThorn 6 #define inf 0x3f3f3f3f 7 using namespace std; 8 9 const int maxn=20000+5,maxm=50000+5; 10 11 int n,m,Q,T,tail,fa[maxn],stk[maxn*1000],ver[20],edg[20],vis[maxm]; 12 13 long long ans[maxm],val[20],w[maxm]; 14 15 struct M{ 16 int x,y,w,flag,id; 17 friend bool operator < (M a,M b){ 18 return a.w<b.w; 19 } 20 }G[20][maxm],lala[maxm],lalala[maxm]; 21 22 struct L{ 23 int id,w; 24 }q[maxm]; 25 26 inline int find(int x){ 27 if(fa[x]==x) 28 return x; 29 stk[++tail]=x,stk[++tail]=fa[x]; 30 return fa[x]=find(fa[x]); 31 } 32 33 inline void merge(int x,int y){ 34 int fx=find(x),fy=find(y); 35 if(fx!=fy) 36 stk[++tail]=fx,stk[++tail]=fx,fa[fx]=fy; 37 } 38 39 inline long long kruskal(int d){ 40 sort(lala+1,lala+edg[d]+1);long long res=0; 41 for(int i=1;i<=edg[d];i++){ 42 int fx=find(lala[i].x),fy=find(lala[i].y); 43 if(fx!=fy) 44 merge(fx,fy),res+=w[lala[i].id],lala[i].flag=1; 45 else 46 lala[i].flag=0; 47 } 48 return res; 49 } 50 51 inline void CDQ(int l,int r,int d){ 52 int tmp=tail,ttmp; 53 if(l==r){ 54 w[q[l].id]=q[l].w; 55 for(int i=1;i<=edg[d-1];i++) 56 lala[i]=G[d-1][i],lala[i].w=w[G[d-1][i].id]; 57 ans[l]=kruskal(d-1)+val[d-1]; 58 while(tmp!=tail) 59 fa[stk[tail-1]]=stk[tail],tail-=2; 60 return; 61 } 62 edg[d]=edg[d-1],val[d]=val[d-1],ver[d]=ver[d-1]; 63 int mid=(l+r)>>1;T++; 64 for(int i=l;i<=r;i++) 65 vis[q[i].id]=T; 66 for(int i=1;i<=edg[d];i++){ 67 lala[i]=G[d-1][i],lala[i].w=w[G[d-1][i].id]; 68 if(vis[G[d-1][i].id]==T) 69 lala[i].w=-inf; 70 } 71 kruskal(d); 72 while(tmp!=tail) 73 fa[stk[tail-1]]=stk[tail],tail-=2; 74 int haha=edg[d]; 75 for(int i=1,j=0;i<=haha;i++){ 76 if(lala[i].flag&&lala[i].w!=-inf) 77 merge(lala[i].x,lala[i].y),ver[d]--,edg[d]--,val[d]+=w[lala[i].id]; 78 else 79 lala[i].x=find(lala[i].x),lala[i].y=find(lala[i].y),G[d][++j]=lala[i],G[d][j].w=w[lala[i].id]; 80 }ttmp=tail; 81 for(int i=1;i<=edg[d];i++){ 82 G[d][i].x=find(G[d][i].x),G[d][i].y=find(G[d][i].y); 83 lala[i]=G[d][i],lala[i].w=w[G[d][i].id]; 84 if(vis[G[d][i].id]==T) 85 lala[i].w=inf; 86 } 87 kruskal(d); 88 while(ttmp!=tail) 89 fa[stk[tail-1]]=stk[tail],tail-=2; 90 haha=edg[d]; 91 for(int i=1,j=0;i<=haha;i++){ 92 if(!lala[i].flag&&lala[i].w!=inf) 93 edg[d]--; 94 else 95 lala[i].x=fa[lala[i].x],lala[i].y=fa[lala[i].y],G[d][++j]=lala[i]; 96 } 97 CDQ(l,mid,d+1),CDQ(mid+1,r,d+1); 98 while(tail!=tmp) 99 fa[stk[tail-1]]=stk[tail],tail-=2; 100 } 101 102 signed main(void){ 103 // freopen("in.txt","r",stdin); 104 // freopen("out.txt","w",stdout); 105 scanf("%d%d%d",&n,&m,&Q); 106 memset(vis,0,sizeof(vis)); 107 for(int i=1;i<=m;i++) 108 scanf("%d%d%d",&G[0][i].x,&G[0][i].y,&G[0][i].w),G[0][i].id=i,w[i]=G[0][i].w; 109 for(int i=1;i<=Q;i++) 110 scanf("%d%d",&q[i].id,&q[i].w); 111 for(int i=1;i<=n;i++) 112 fa[i]=i; 113 edg[0]=m,ver[0]=n,CDQ(1,Q,1); 114 for(int i=1;i<=Q;i++) 115 printf("%lld\n",ans[i]); 116 return 0; 117 }//Cap ou pas cap. Cap.
[BZOJ2716 平面最近点]:
2716: [Violet 3]天使玩偶
Time Limit: 80 Sec Memory Limit: 128 MBSubmit: 1476 Solved: 623
[Submit][Status][Discuss]
Description
Input
Output
分析:
和第一道题目是一样的,只不过是把求和变成了取max...
首先我们可以以当前询问的点为原点,把平面划分为4个部分,我们首先考虑左下角的点dis[i,j]=x[i]-x[j]+y[i]-y[j]=x[i]+y[i]-(x[j]+y[j])...
这样我们只需要查询当前点左下角的点x+y最大的点即可,那么还是按照x排序维护y的树状数组...
那么其他三个区域的点可以对称一下转化为左下角的点...
但是这题它卡常数...我人帅自带大常数TAT...T死了...
所以就把O(nlgn)的四个排序改成了四个memcpy...然后就A了QAQ...
代码:
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 //by NeighThorn 6 #define inf 0x7fffffff 7 using namespace std; 8 //大鹏一日同风起,扶摇直上九万里 9 10 const int maxn=1000000+5,maxm=1000000+5; 11 12 int n,m,T,Max,tr[maxm],tim[maxm],ans[maxm]; 13 14 struct M{ 15 int t,x,y,id; 16 bool operator <(const M &a)const{ 17 if(x!=a.x) 18 return x<a.x; 19 return t<a.t; 20 } 21 }q[maxn],q2[maxn],l[maxn]; 22 23 inline int read(void){ 24 char ch=getchar();int f=1,x=0; 25 while(!(ch>='0'&&ch<='9')){ 26 if(ch=='-') 27 f=-1; 28 ch=getchar(); 29 } 30 while(ch>='0'&&ch<='9') 31 x=x*10+ch-'0',ch=getchar(); 32 return f*x; 33 } 34 35 inline void insert(int x,int y){ 36 for(;x<=Max;x+=x&-x){ 37 /* 38 if(tim[x]!=T) 39 tr[x]=-1; 40 tim[x]=T,tr[x]=max(tr[x],y); 41 */ 42 if(tim[x]!=T||tr[x]<y) 43 tr[x]=y,tim[x]=T; 44 } 45 } 46 47 inline int query(int x){ 48 int res=-1; 49 for(;x;x-=x&-x) 50 if(tim[x]==T) 51 res=max(tr[x],res); 52 return res; 53 } 54 55 /* 56 inline void CDQ(int l,int r){ 57 if(l==r) 58 return; 59 int mid=(l+r)>>1,l1=l,l2=mid+1; 60 for(int i=l;i<=r;i++){ 61 if(q[i].id<=mid) 62 q2[l1++]=q[i]; 63 else 64 q2[l2++]=q[i]; 65 } 66 for(int i=l;i<=r;i++) 67 q[i]=q2[i]; 68 CDQ(l,mid);int j=l;T++; 69 for(int i=mid+1;i<=r;i++){ 70 for(;q[j].x<=q[i].x&&j<=mid;j++) 71 if(q[j].t==1) 72 insert(q[j].y,q[j].x+q[j].y); 73 if(q[i].t==2){ 74 int lala=query(q[i].y); 75 q[i].ans=lala==-1?q[i].ans:min(q[i].x+q[i].y-lala,q[i].ans); 76 } 77 } 78 CDQ(mid+1,r); 79 l1=l,l2=mid+1; 80 for(int i=l;i<=r;i++){ 81 if((q[l1].x<q[l2].x&&l1<=mid)||l2>r) 82 q2[i]=q[l1++]; 83 else 84 q2[i]=q[l2++]; 85 } 86 for(int i=l;i<=r;i++) 87 q[i]=q2[i]; 88 } 89 */ 90 91 inline void CDQ(int l,int r){ 92 if(l==r) 93 return; 94 int mid=(l+r)>>1,l1=l,l2=mid+1; 95 CDQ(l,mid),CDQ(mid+1,r); 96 for(int i=l;i<=r;i++){ 97 if((l1<=mid&&q[l1]<q[l2])||l2>r) 98 q2[i]=q[l1++]; 99 else 100 q2[i]=q[l2++]; 101 } 102 for(int i=l;i<=r;i++) 103 q[i]=q2[i]; 104 T++; 105 for(int i=l;i<=r;i++){ 106 if(q[i].t==2&&q[i].id>mid){ 107 int lala=query(q[i].y); 108 ans[q[i].id]=lala==-1?ans[q[i].id]:min(q[i].x+q[i].y-lala,ans[q[i].id]); 109 } 110 else if(q[i].t==1&&q[i].id<=mid) 111 insert(q[i].y,q[i].x+q[i].y); 112 } 113 } 114 115 signed main(void){ 116 // freopen("in.txt","r",stdin); 117 // freopen("out.txt","w",stdout); 118 n=read(),m=read();Max=0;T=0;m+=n; 119 for(int i=1;i<=n;i++){ 120 l[i].x=read()+1,l[i].y=read()+1,l[i].id=i,l[i].t=1; 121 if(l[i].x>Max) 122 Max=l[i].x; 123 if(l[i].y>Max) 124 Max=l[i].y; 125 } 126 for(int i=n+1;i<=m;i++){ 127 l[i].t=read(),l[i].x=read()+1,l[i].y=read()+1,l[i].id=i,ans[i]=inf; 128 if(l[i].x>Max) 129 Max=l[i].x; 130 if(l[i].y>Max) 131 Max=l[i].y; 132 } 133 Max++; 134 135 /* 136 sort(q+1,q+m+1);CDQ(1,m); 137 for(int i=1;i<=m;i++) 138 q[i].x=Max-q[i].x; 139 sort(q+1,q+m+1);CDQ(1,m); 140 for(int i=1;i<=m;i++) 141 q[i].y=Max-q[i].y; 142 sort(q+1,q+m+1);CDQ(1,m); 143 for(int i=1;i<=m;i++) 144 q[i].x=-(q[i].x-Max); 145 sort(q+1,q+m+1);CDQ(1,m); 146 */ 147 148 memcpy(q,l,sizeof(q)); 149 CDQ(1,m); 150 151 memcpy(q,l,sizeof(q)); 152 for(int i=1;i<=m;i++) 153 q[i].x=Max-q[i].x; 154 CDQ(1,m); 155 156 memcpy(q,l,sizeof(q)); 157 for(int i=1;i<=m;i++) 158 q[i].y=Max-q[i].y; 159 CDQ(1,m); 160 161 memcpy(q,l,sizeof(q)); 162 for(int i=1;i<=m;i++) 163 q[i].x=Max-q[i].x,q[i].y=Max-q[i].y; 164 CDQ(1,m); 165 166 for(int i=n+1;i<=m;i++) 167 if(l[i].t==2) 168 printf("%d\n",ans[i]); 169 return 0; 170 }//Cap ou pas cap. Cap.
By NeighThorn