bzoj2683简单题

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cmath>
 4 #include <algorithm>
 5 #include <cstring>
 6 #define maxn 500005
 7 #define maxm 800005 
 8 using namespace std;
 9 
10 int n,m,cnt,sum[maxn],pos[maxm],ans[maxm];
11 struct date{
12     int op,x,y,v,id;
13 }qs[maxm];
14 
15 bool comp(date x,date y){
16     return x.x<y.x;
17 }
18 
19 int lowbit(int x){
20     return x&(-x);
21 }
22 
23 void add(int x,int y){
24     for (int i=x;i<=n;i+=lowbit(i)){
25         sum[i]+=y;
26     }
27 }
28 
29 int  query(int x){
30     int temp=0;
31     for (int i=x;i>0;i-=lowbit(i)){
32         temp+=sum[i];
33     }
34     return temp;
35 }
36 
37 void cdq_solve(int l,int r){
38     if (l==r) return;
39     int mid=(l+r)/2,temp=0;
40     cdq_solve(l,mid),cdq_solve(mid+1,r);
41     sort(qs+l,qs+mid+1,comp),sort(qs+mid+1,qs+r+1,comp);
42     int i=l,j=mid+1;
43     while (j<=r){
44         while (qs[i].op==2&&i<=mid) i++;
45         while (qs[j].op==1&&j<=r) j++;
46         if (i<=mid&&qs[i].x<=qs[j].x) add(qs[i].y,qs[i].v),i++,temp=i-1;
47         else if (j<=r) ans[qs[j].id]+=query(qs[j].y),j++;
48     }
49     for (int t=l;t<=temp;t++) if (qs[t].op==1) add(qs[t].y,-qs[t].v); 
50 }
51 
52 int main(){
53     memset(ans,0,sizeof(ans));
54     memset(sum,0,sizeof(sum));
55     int op,x1,x2,y1,y2;
56     scanf("%d",&n),m=cnt=0;
57     for (;;){
58         scanf("%d",&op);
59         if (op==1){
60             qs[++m].op=op,qs[m].id=m;
61             scanf("%d%d%d",&qs[m].x,&qs[m].y,&qs[m].v);
62         }else{
63             if (op==2){
64                 scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
65                 pos[++cnt]=m;
66                 qs[++m].op=op,qs[m].x=x1-1,qs[m].y=y1-1,qs[m].id=m;
67                 qs[++m].op=op,qs[m].x=x2,qs[m].y=y2,qs[m].id=m;        
68                 qs[++m].op=op,qs[m].x=x1-1,qs[m].y=y2,qs[m].id=m;
69                 qs[++m].op=op,qs[m].x=x2,qs[m].y=y1-1,qs[m].id=m;
70             }else break;
71         }
72     }
73     cdq_solve(1,m);
74     for (int i=1;i<=cnt;i++) printf("%d\n",ans[pos[i]+1]+ans[pos[i]+2]-ans[pos[i]+3]-ans[pos[i]+4]);
75 }
View Code

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2683

题目大意:给定一个n*n的矩形,以及若干个操作,操作有如下两种:

1.给矩形的(x,y)加上一个v;

2.询问某个子矩阵的权值和。

数据范围:n<=5*10^5,操作数<=2*10^5;

初看这题,尼玛这不是二维树状数组(二维线段树也行,不过是作死)傻逼题吗,期望复杂度O(nlog^2 n),空间复杂度n^2,再一看数据范围,GG。

在ls神犇的教导下,我学会了四种通过此题的方法:1.二维线段树动态开点,空间复杂度nlog^2 n),有点卡空间。

2.二维树状数组+hash表,可惜我没掌握这种正确姿势-.-;

3.线段树套平衡树,线段树维护x坐标,每一层套一个splay,以y坐标为关键字,对于一个询问,拆成四个矩阵,再在对应的线段树中在splay中去找,更新答案即可,据说这种写法空间复杂度只有O(n)的复杂度,可以过。

4.进入正题吧,这题的最佳姿势是cdq分治+树状数组维护区间和;

前三种做法略过(在线做法),说说第四种做法(离线做法)吧:

我先来说说cdq_分治的流程吧,cdq分治一般是解决具有一下两种性质的问题(只有修改和查询操作):

1.只能用离线做法。

2.每个修改操作对询问的影响是独立的,即与之前的修改操作无关。

cdq分治与一般的分治不同,一般的分治分出来的子区间是独立的,个个击破即可,而cdq分治分出来的两个子区间是相互联系的。(以下的分治都是指cdq分治)

由于在该问题中,每个询问只与在此之前的修改操作有关。

对于区间l,mid,记为区间1,区间mid+1,r,记为区间2,

过程:

1.递归处理区间(1),

2.递归处理区间(2),

3.统计区间1中的修改操作对区间2中的询问操作的影响。

4.算法结束。

关键在于第3步,对于这个题,我们将两个区间各自按x坐标排序,然后用两个指针分别去扫一遍,遇到区间1中的修改操作,就把纵坐标加入树状数组,然后及时统计区间2中询问的答案即可。具体细节可见代码。

cdq分治+树状数组

 

posted @ 2016-05-18 20:22  oyzx~  阅读(721)  评论(1编辑  收藏  举报