Bzoj2683 简单题 [CDQ分治]

Time Limit: 50 Sec  Memory Limit: 128 MB
Submit: 1071  Solved: 428

Description

你有一个N*N的棋盘,每个格子内有一个整数,初始时的时候全部为0,现在需要维护两种操作:


命令


参数限制


内容


1 x y A


1<=x,y<=N,A是正整数


将格子x,y里的数字加上A


2 x1 y1 x2 y2


1<=x1<= x2<=N

1<=y1<= y2<=N


输出x1 y1 x2 y2这个矩形内的数字和


3



终止程序

Input

输入文件第一行一个正整数N。

接下来每行一个操作。

Output

对于每个2操作,输出一个对应的答案。

Sample Input

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

1<=N<=500000,操作数不超过200000个,内存限制20M。

对于100%的数据,操作1中的A不超过2000。

Source

CDQ分治

第4遍回顾之前抄的代码的时候,突然顿悟。

个人理解,这种分治方法类似于做矩形面积并时候用到的扫描线法。将每个区间修改操作拆成插入/删除,和每个询问操作一起按横坐标x升序排序。

用一个一维数组记录“当前横坐标”对应的y轴情况,从左往右扫描所有操作,并用差分的方式完成修改(在时间维度上差分),记录答案。

↑该一维数组可以用树状数组优化,扫描操作可以用分治方法优化(每层分治时处理前半部分操作对后半部分查询的影响)。

  ↑组合起来就成了CDQ分治。

________

PS1: 这时我想起,两三个个月前RLQ说他研究出一种用树状数组乱搞二维大数据的做法,当时没怎么听懂,也没太在意……卧槽,原来是CDQ分治?

    CDQ分治要是晚出现两年,就变成RLQ分治了……%%%%%

PS2:   之前抄的LCT也已经回顾了10+遍了,是不是也快要顿悟了呢……

________

 1 /*by SilverN*/
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cstdio>
 6 #include<cmath>
 7 using namespace std;
 8 const int mxn=200010;
 9 int read(){
10     int x=0,f=1;char ch=getchar();
11     while(ch<‘0‘ || ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
12     while(ch>=‘0‘ && ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
13     return x*f;
14 }
15 int n;
16 struct opt{
17     int flag;
18     int x,y,w;
19     int t;
20     int id;
21 }a[mxn*10],b[mxn*10];
22 int cnt=0;
23 int cmp(opt q,opt e){
24     if(q.x==e.x){
25         if(q.y==e.y)return q.flag<e.flag;
26         return q.y<e.y;
27     }
28     return q.x<e.x;
29 }
30 int ans[mxn];
31 int t[mxn*5];
32 void add(int x,int v){while(x<=n){t[x]+=v;x+=x&-x;}return;}
33 int sum(int x){
34     int res=0;
35     while(x){res+=t[x];x-=x&-x;}
36     return res;
37 }
38 void solve(int l,int r){
39     if(l>=r)return;
40     int i,j,mid=(l+r)>>1;
41     int l1=l,l2=mid+1;
42     for(i=l;i<=r;i++){
43         if(a[i].flag==1 && a[i].t<=mid) add(a[i].y,a[i].w);
44         else if(a[i].flag==2 && a[i].t>mid) ans[a[i].id]+=sum(a[i].y)*a[i].w;
45     }
46     for(i=l;i<=r;i++)
47         if(a[i].flag==1 && a[i].t<=mid) add(a[i].y,-a[i].w);
48     for(i=l;i<=r;i++)
49         if(a[i].t<=mid)b[l1++]=a[i];
50         else b[l2++]=a[i];
51     for(i=l;i<=r;i++)a[i]=b[i];
52     solve(l,mid);solve(mid+1,r);
53     return;
54 }
55 int main(){
56     n=read();
57     int i,j,x,y,c,v;
58     int id=0;
59     while(1){
60         c=read();
61         if(c==3)break;
62         if(c==1){//修改
63             x=read();y=read();v=read();
64             a[++cnt].flag=1;a[cnt].x=x;a[cnt].y=y;a[cnt].w=v;
65             a[cnt].t=cnt;
66         }
67         else{//查询
68             x=read();y=read();c=read();v=read();
69             a[++cnt].flag=2;a[cnt].x=x-1;a[cnt].y=y-1;
70                 a[cnt].w=1;a[cnt].t=cnt;a[cnt].id=++id;
71             a[++cnt].flag=2;a[cnt].x=x-1;a[cnt].y=v;
72                 a[cnt].w=-1;a[cnt].t=cnt;a[cnt].id=id;
73             a[++cnt].flag=2;a[cnt].x=c;a[cnt].y=y-1;
74                 a[cnt].w=-1;a[cnt].t=cnt;a[cnt].id=id;
75             a[++cnt].flag=2;a[cnt].x=c;a[cnt].y=v;
76                 a[cnt].w=1;a[cnt].t=cnt;a[cnt].id=id;
77         }
78     }
79     sort(a+1,a+cnt+1,cmp);
80     solve(1,cnt);
81     for(i=1;i<=id;i++){
82         printf("%d\n",ans[i]);
83     }
84     return 0;
85 }
时间: 2024-10-04 03:50:10

Bzoj2683 简单题 [CDQ分治]的相关文章

bzoj2683简单题 cdq分治

2683: 简单题 Time Limit: 50 Sec  Memory Limit: 128 MBSubmit: 1803  Solved: 731[Submit][Status][Discuss] Description 你有一个N*N的棋盘,每个格子内有一个整数,初始时的时候全部为0,现在需要维护两种操作: 命令 参数限制 内容 1 x y A 1<=x,y<=N,A是正整数 将格子x,y里的数字加上A 2 x1 y1 x2 y2 1<=x1<= x2<=N 1<

bzoj 2683 简单题 cdq分治

题面 题目传送门 解法 可以离线,那么就是非常简单的cdq分治了 只要把询问拆成4个,然后就变成了一个三维偏序问题 时间复杂度:\(O(q\ log^2\ n)\) 代码 #include <bits/stdc++.h> #define int long long #define N 1000010 using namespace std; template <typename node> void chkmax(node &x, node y) {x = max(x, y

【BZOJ-1176&amp;2683】Mokia&amp;简单题 CDQ分治

1176: [Balkan2007]Mokia Time Limit: 30 Sec  Memory Limit: 162 MBSubmit: 1854  Solved: 821[Submit][Status][Discuss] Description 维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000. Input 第一行两个整数,S,W;其中S为矩阵初始值;W为矩阵大小

BZOJ 2683 简单题 ——CDQ分治

简单题 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i) #define D(i,j,k) for (int i=j;i>=k;--i) #define maxn 2000005 int sum[maxn]; void a

BZOJ 2683 简单题 cdq分治+树状数组

题意:链接 **方法:**cdq分治+树状数组 解析: 首先对于这道题,看了范围之后,二维的数据结构是显然不能过的,于是我们可能会考虑把一维排序之后另一位上数据结构什么的,然而cdq分治却能够很好的体现它的作用. 首先,对于每一个询问求和,显然是x在它左边的并且出现时间在它之前的所有的change对他可能会有影响. 我们按照x第一关键字,y第二关键字,操作第三关键字来排序所有的询问,然后在cdq的时候,每次递归处理左半区间,按照x动态的将y这一列的值加到树状数组里,来更新右半边的所有询问,注意这

BZOJ2683: 简单题(CDQ分治 + 树状数组)

BZOJ2683: 简单题(CDQ分治 + 树状数组) 题意: 你有一个\(N*N\)的棋盘,每个格子内有一个整数,初始时的时候全部为\(0\),现在需要维护两种操作: 命令 参数限制 内容 \(1\ x\ y\ A\) \(1\le x,y \le N\),A是正整数 将格子\(x,y\)里的数字加上\(A\) \(2\ x1\ y1\ x2\ y2\) \(1\le x1\le x2\le N,1\le y1\le y2\le N\) 输出\(x1\ y1\ x2\ y2\)这个矩形内的数字

bzoj2683简单题

2683: 简单题 Time Limit: 50 Sec  Memory Limit: 128 MB Submit: 738  Solved: 307 [Submit][Status][Discuss] Description 你有一个N*N的棋盘,每个格子内有一个整数,初始时的时候全部为0,现在需要维护两种操作: 命令 参数限制 内容 1 x y A 1<=x,y<=N,A是正整数 将格子x,y里的数字加上A 2 x1 y1 x2 y2 1<=x1<= x2<=N 1<

bzoj2683: 简单题

CDQ分治,然而超过内存了 CDQ分治的思想还是很有趣的. http://www.lydsy.com/JudgeOnline/problem.php?id=2683 /************************************************************** Problem: 2683 User: 1349367067 Language: C++ Result: Accepted Time:7344 ms Memory:45028 kb ************

2019 南昌网络赛icpc I题 cdq分治或分块

题意:给你一个数组,然后每次有两种操作,操作一是修改数组里的数,操作二是查询区间[ l , r ] 里有多少个子区间满足以下条件:1.子区间内的数全部相同.2.子区间内的数在x到y之间.3.子区间得是不能延伸的. 题目链接:https://nanti.jisuanke.com/t/41356 题解:首先转化问题,设 b[ i ] = a[i]==a[i-1] ? 0 : a[i],然后问题就变成了问询区间内有多少个x到y之间的数.(注意左端点特判)这不就是主席树....带修改...好,树状数组加