初识CDQ分治

[BZOJ 1176:单点修改,查询子矩阵和]:

1176: [Balkan2007]Mokia

Time Limit: 30 Sec  Memory Limit: 162 MB
Submit: 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

0 4
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3

Sample Output

3
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
",lala);
 98         }
 99     return 0;
100 }//Cap ou pas cap. Cap.
View Code

 

[BZOJ 3163:背包问题]:

3163: [Heoi2013]Eden的新背包问题

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 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

5
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

13
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
",ans[i]);
53     return 0;
54 }//Cap ou pas cap. Cap.
View Code

[BZOJ 3237: 无向图是否联通]:

3237: [Ahoi2013]连通图

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 1155  Solved: 396
[Submit][Status][Discuss]

Description

Input

Output

Sample Input

4 5
1 2
2 3
3 4
4 1
2 4
3
1 5
2 2 3
2 1 2

Sample Output

Connected
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.
View Code

[BZOJ 2001:动态最小生成树]:

2001: [Hnoi2010]City 城市建设

Time Limit: 20 Sec  Memory Limit: 162 MB
Submit: 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

5 5 3
1 2 1
2 3 2
3 4 3
4 5 4
5 1 5
1 6
1 1
5 3

Sample Output

14
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
",ans[i]);
116     return 0;
117 }//Cap ou pas cap. Cap.
View Code

[BZOJ2716 平面最近点]:

2716: [Violet 3]天使玩偶

Time Limit: 80 Sec  Memory Limit: 128 MB
Submit: 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
",ans[i]);
169     return 0;
170 }//Cap ou pas cap. Cap.
View Code

By NeighThorn

原文地址:https://www.cnblogs.com/neighthorn/p/6242059.html