传送门

时间限制:2 s 内存限制:128 MB

DESCRIPTION

C国国土辽阔,地大物博......但是最近却在闹蝗灾.....

我们可以把C国国土当成一个W×W的矩阵,你会收到一些诸如(X,Y,Z)的信息,代表(X,Y)这个点增多了Z只蝗虫,而由于C国政府机关比较臃肿,为了批复消灭蝗虫的请求需要询问一大堆的问题......每个询问形如(X1,Y1,X2,Y2),询问在(X1,Y1,X2,Y2)范围内有多少蝗虫(请注意询问不会改变区域内的蝗虫数),你作为一个C国的程序员,需要编一个程序快速的回答所有的询问。

NOTICE

C国一开始没有蝗虫。

INPUT

输入文件的第一行包括一个整数W,代表C国国土的大小。第二行有一个整数N,表示事件数。接下来有N行表示N个事件,以(1 X Y Z)的形式或(2,X1,Y1,X2,Y2)的形式给出,分别代表蝗虫的增加和询问。

OUTPUT

对于每个询问输出一个整数表示需要的结果。

SAMPLE INPUT

locust.in

5

8

2 4 1 4 2

1 3 1 8

1 4 4 4

2 1 3 4 4

1 1 5 1

1 4 4 5

2 2 2 5 4

2 3 2 4 4

SAMPLE OUTPUT

locust.out

0

4

9

9

数据范围:

10%的数据满足W<=100,N<=100;

30%的数据满足W<=2000,N<=5000;

50%的数据满足W<=100000,N<=50000;

100%的数据满足W<=500000,N<=200000,每次蝗虫增加数不超过1000;

时间限制:

2s


这道题必须好好写一写. 本来是 CDQ 分治的入门题, 然而我调了半天.

CDQ 分治是处理多维偏序问题的一种方法.

这是别人总结的, 但我认为这样讲比较狭隘. 在有的问题中, CDQ 分治降低复杂度的核心并不是利用某一维有序, 而是纯粹为了共用我们要维护的某个动态集合中的共有元素. 这样的题目比如...

概念

操作操作序列

操作包括查询和修改, 通常可以用一个 tuple(n元组)表示, 比如 $(x, y, z)$. 我们利用 CDQ 分治处理其中已经有序的某一维(这一维可能自然有序, 比如, 时间;也可能需要先将操作序列按这一维排好序 (预处理)),分治时再对另外某一维排序,对第三维用数据结构处理。

注意

这里用于处理第三维的数据结构不能在用之前直接清空, 而要在用后消除已经进行的修改, 这样才能保证维护数据结构的复杂度与当前处理的操作序列的长度是线性关系。(我就在这一点上T到死...)

Solution

修改操作用 $(x, y, \mathrm{val})$ 表示, 查询操作用 $(x_1, y_1, x_2, y_2, \mathrm{id})$ 表示, 其中 $\mathrm{id}$ 表示修改操作的序号.

我们将所有修改与查询操作按时间分治. 令函数 $solve(l, r)$ 处理操作序列 $[l, r)$. 我们将 $[l, r)$ 分成两部分 $[l,\mathrm{mid})$ 和 $[\mathrm{mid}, r)$. 对前半部分 $[l, \mathrm{mid})$ 的处理完全是个子问题,直接调用 $solve(l, \mathrm{mid})$. 我们考虑如何计算前半部分中的修改操作对后半部分中的查询操作的答案的影响 (或称贡献):

我们将前半部分中的修改操作和后半部分中的查询操作放在一起按他们的 $x$ 坐标排序.

具体的做法是:

将后一半操作序列中的每个查询操作 $(x_1, y_1, x_2, y_2, id)$ 拆成两个: $(x_1-1, y_1, y_2, id, -1)$ 和 $(x_2, y_1, y_2, id, 1)$

然后把前一半中的修改操作和后一半中拆分后的查询操作放在一起 (即放在一个数组里) 按它们的$x$坐标排序, 对于 $x$ 相同的两个操作, 修改操作要排在查询操作的前面 (这也是个坑点).

从左到右扫这个数组,也就相当于一条垂直于 $x$ 轴的扫描线从左往右扫描。

在这个过程中我们用树状数组来维护 $y$ 方向的前缀和。(详见代码...)

最后要把在此过程中对树状数组的修改逐个消除。而不应该在开始扫描数组之前将树状数组清空, 那样做复杂度将是 $O(NW)$ 的, 必然会T.

Implementation

#include <bits/stdc++.h>
using namespace std; const int N{1<<19}, M{1<<18}; // void scanf ( int& x , char c = 0 ) {
// while ( ( c = getchar () ) < '0' || c > '9' ) ;
// x = c - '0' ;
// while ( ( c = getchar () ) >= '0' && c <= '9' ) x = x * 10 + c - '0' ;
// } struct BIT{
int bit[N];
int n;
void init(int x){
n=x;
// memset(bit+1, 0, n*sizeof(int)); //tricky
}
int sum(int x){
int res=0;
for(; x; res+=bit[x], x-=x&-x);
return res;
}
void add(int x, int v){
for(; x && x<=n; bit[x]+=v, x+=x&-x);
}
}bit; struct Q{
int x1, y1, x2, y2, id;
bool operator<(const Q &rhs)const{
if(x1!=rhs.x1)
return x1<rhs.x1;
return y2<rhs.y2; //error-prone
}
}q[M], a[M<<1]; int res[M]; void DC(int l, int r){
// cout<<l<<' '<<r<<endl;
if(l+1==r){
return; //caution [l, r) //error-prone
} int mid=l+r>>1;
DC(l, mid);
DC(mid, r); //caution: not DC(mid+1, r) int tail=0; for(int i=l; i<mid; i++){
if(q[i].y2 == -1){ //add
a[tail++]=q[i];
}
} for(int i=mid; i<r; i++){
if(q[i].y2 != -1){ //query
// a[tail++]=q[i];
a[tail++]=q[i];
a[tail++]=q[i];
a[tail-1].x1--; //sorted by x1
a[tail-2].x1=a[tail-2].x2;
}
} sort(a, a+tail); for(int i=0; i<tail; i++){
//x2: the value to add
if(a[i].y2==-1) bit.add(a[i].y1, a[i].x2);
else{
// we should make sure that all add operations a[j] with a[j].x<=a[i].x are placed before a[i];
if(a[i].x1==a[i].x2){
res[a[i].id]+=bit.sum(a[i].y2)-bit.sum(a[i].y1-1);
}
else{
res[a[i].id]-=bit.sum(a[i].y2)-bit.sum(a[i].y1-1);
}
}
} for(int i=l; i<mid; i++)
if(q[i].y2==-1) bit.add(a[i].y1, -q[i].x2); } int main(){
freopen("locust.in", "r", stdin);
freopen("locust.out", "w", stdout);
int n, m; // scanf(n), scanf(m);
scanf("%d%d", &n, &m);
bit.init(n); int id=0, tail=0;
int nq=0;
for(int t, i=0; i<m; i++){
scanf("%d", &t);
if(t==1){
// scanf(q[i].x1), scanf(q[i].y1), scanf(q[i].x2);
scanf("%d%d%d", &q[i].x1, &q[i].y1, &q[i].x2);
q[i].y2=-1;
}
else{
// scanf(q[i].x1), scanf(q[i].y1), scanf(q[i].x2), scanf(q[i].y2);
scanf("%d%d%d%d", &q[i].x1, &q[i].y1, &q[i].x2, &q[i].y2);
}
q[i].id=i;
} DC(0, m);
for(int i=0; i<m; i++)
if(q[i].y2 != -1)
printf("%d\n", res[i]);
}

典型的错误写法 (会TLE):

#include <bits/stdc++.h>
using namespace std; const int N{1<<19}, M{1<<18}; struct BIT{
int bit[N];
int n;
void init(int x){
n=x;
memset(bit+1, 0, n*sizeof(int)); //tricky
}
int sum(int x){
int res=0;
for(; x; res+=bit[x], x-=x&-x);
return res;
}
void add(int x, int v){
for(; x && x<=n; bit[x]+=v, x+=x&-x);
}
}bit; struct Q{
int x1, y1, x2, y2, id;
bool operator<(const Q &rhs)const{
if(x1!=rhs.x1)
return x1<rhs.x1;
return y2<rhs.y2; //error-prone
}
}q[M<<1], a[M<<1]; int res[M]; void DC(int l, int r){
// cout<<l<<' '<<r<<endl;
if(l+1==r){
return; //caution [l, r) //error-prone
}
int mid=l+r>>1;
int tail=0;
int max_y=0; for(int i=l; i<mid; i++){
if(q[i].y2 == -1){ //add
a[tail++]=q[i];
max_y=max(max_y, q[i].y1);
}
} for(int i=mid; i<r; i++){
if(q[i].y2 != -1){ //query
// a[tail++]=q[i];
max_y=max(max_y, q[i].y2);
a[tail++]=q[i];
a[tail++]=q[i];
// 2018/3/8 UPD 这样标记太蠢了!直接用 x2 的两个不同值标记!
// 由于 x1 减少的一 x1==x2 对于“前半询问”一定不成立
a[tail-1].x1--; //sorted by x1
// x1==x2 标志着该“半询问”是原询问的后一项
a[tail-2].x1=a[tail-2].x2;
}
} sort(a, a+tail);
bit.init(max_y); for(int i=0; i<tail; i++){
//x2: the value to add
if(a[i].y2==-1) bit.add(a[i].y1, a[i].x2);
else{
// we should make sure that all add option a[j] with a[j].x<=a[i].x are placed before a[i];
if(a[i].x1==a[i].x2){
res[a[i].id]+=bit.sum(a[i].y2)-bit.sum(a[i].y1-1);
}
else{
res[a[i].id]-=bit.sum(a[i].y2)-bit.sum(a[i].y1-1);
}
}
} DC(l, mid);
DC(mid, r); //caution: not DC(mid+1, r)
} int main(){
freopen("locust.in", "r", stdin);
freopen("locust.out", "w", stdout);
int n, m; cin>>n>>m; int id=0, tail=0;
int nq=0;
for(int t, i=0; i<m; i++){
scanf("%d", &t); if(t==1){
scanf("%d%d%d", &q[i].x1, &q[i].y1, &q[i].x2);
q[i].y2=-1;
}
else{
scanf("%d%d%d%d", &q[i].x1, &q[i].y1, &q[i].x2, &q[i].y2);
}
q[i].id=i; } DC(0, m);
for(int i=0; i<m; i++)
if(q[i].y2 != -1)
printf("%d\n", res[i]);
}

COGS 577 蝗灾的更多相关文章

  1. cogs 577 蝗灾 CDQ分治

    第一道CDQ,抄了下helenkeller的代码,感觉和归并排序差不多... 因为左半边的修改肯定在右半边的询问之前,所以就不用管时间的限制了,可以直接x轴排序树状数组处理y轴... #include ...

  2. COGS 577 蝗灾 [CDQ分治入门题]

    题目链接 昨天mhr神犇,讲分治时的CDQ分治的入门题. 题意: 你又一个w*w正方形的田地. 初始时没有蝗虫. 给你两个操作: 1. 1 x y z: (x,y)这个位置多了z只蝗虫. 2. 2 x ...

  3. COGS 577 蝗灾 线段树+CDQ分治

    第一次写cdq分治 感谢hhd&lty 这20亿对CP的指导(逃) 其实 就是 递归看左半部分对右半部分的贡献 (树状数组写挂了--临时改的线段树[大写的尴尬]) //By SiriusRen ...

  4. 1Z0-053 争议题目解析577

    1Z0-053 争议题目解析577 考试科目:1Z0-053 题库版本:V13.02 题库中原题为: 577.What methods are available to recover lost co ...

  5. 【COGS 254】【POI 2001】交通网络图

    http://www.cogs.top/cogs/problem/problem.php?pid=254 dist[i]表示能最早到达i点的时间.这样就可以用最短路模型来转移了. #include&l ...

  6. 【COGS】894. 追查坏牛奶

    http://cojs.tk/cogs/problem/problem.php?pid=894 题意:n个点m条边的加权网络,求最少边数的按编号字典序最小的最小割.(n<=32, m<=1 ...

  7. 【COGS】147. [USACO Jan08] 架设电话线(二分+spfa)

    http://cojs.tk/cogs/problem/problem.php?pid=147 学到新姿势了orz 这题求的是一条1-n的路径的最大路径最小. 当然是在k以外的. 我们可以转换一下. ...

  8. 【COGS & USACO Training】710. 命名那个数字(hash+水题+dfs)

    http://cojs.tk/cogs/problem/problem.php?pid=710 近日开始刷水... 此题我为了练一下hash...但是hash跑得比暴力还慢.. 不言而喻... #in ...

  9. 【COGS & USACO】896. 圈奶牛(凸包)

    http://cojs.tk/cogs/problem/problem.php?pid=896 我的计算几何入门题... 看了看白书的计算几何部分,,恩好嘛.. 乃们都用向量!!!! 干嘛非要将2个点 ...

随机推荐

  1. React Native开发技术周报1

    (一).资讯 1.React Native 0.21版本发布,最新版本功能特点,修复的Bug可以看一下已翻译 重要:如果升级 Android 项目到这个版本一定要读! 我们简化了 Android 应用 ...

  2. Jquery和JS获取ul中li标签(转)

    js 获取元素下面所有的li var content=document.getElementById("content"); var items=content.getElemen ...

  3. ModernUI教程:MEF应用向导

    本文主要说明在Modern UI框架下使用MEF的必要步骤,关于MEF请自行脑补. MEF-INTO-MUI实例代码下载: MefMuiApp.zip 1:创建一个导出属性 ModernFrame用来 ...

  4. NPOI2.0学习(三)

    HSSFWorkbook wk = new HSSFWorkbook();//你用来操作的HSSFWorkbook的实例 ICellStyle cellStyle = wk.CreateCellSty ...

  5. 关于insert /*+ append*/ 各种insert插入速度比较

    来源于:http://www.cnblogs.com/rootq/archive/2009/02/11/1388043.html SQL> select count(*) from t;COUN ...

  6. 检测当前网段哪些IP是在线的

    [root@storage ~]# cat ping.ip #!/bin/bashfor ip in `seq 1 255`    do    {      ping -c 2 192.168.220 ...

  7. [转]浅谈jQuery EasyUI的属性设置

    原文地址:http://www.easyui.info/archives/1664.html 对jQuery EasyUI有一定了解的话,应该知道基本上每一个组件都有一个"options&q ...

  8. 【转】向HTML中插入视频并兼容所有浏览器的方法

    原文地址:http://www.jb51.net/web/168548.html 向HTML中插入视频有两种方法,一种是古老的object标签,一种是html5中的video标签,前者兼容性相对好些, ...

  9. 【BZOJ 1043】【HNOI 2008】下落的圆盘 判断圆相交+线段覆盖

    计算几何真的好暴力啊. #include<cmath> #include<cstdio> #include<cstring> #include<algorit ...

  10. AndroidPn

    客户端的主要包说明 org.androidpn.client包下的文件 public class Constants {  //包含静态数据 public class InvalidFormatExc ...