传送门

时间限制: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. Java 集合与队列的插入、删除在并发下的性能比较

    这两天在写一个java多线程的爬虫,以广度优先爬取网页,设置两个缓存: 一个保存已经访问过的URL:vistedUrls 一个保存没有访问过的URL:unVistedUrls 需要爬取的数据量不大,对 ...

  2. lecture16-联合模型、分层坐标系、超参数优化及本课未来的探讨

    这是HInton的第16课,也是最后一课. 一.学习一个图像和标题的联合模型 在这部分,会介绍一些最近的在学习标题和描述图片的特征向量的联合模型上面的工作.在之前的lecture中,介绍了如何从图像中 ...

  3. Theano2.1.12-基础知识之使用GPU

    来自:http://deeplearning.net/software/theano/tutorial/using_gpu.html using the GPU 想要看GPU的介绍性的讨论和对密集并行 ...

  4. C#基础之泛型

    1.泛型的本质 泛型的好处不用多说,在.NET中我看到有很多技术都是以泛型为基础的,不过因为不懂泛型而只能对那些技术一脸茫然.泛型主要用于集合类,最主要的原因是它不需要装箱拆箱且类型安全,比如很常用的 ...

  5. <实训|第五天>通过搭建NFS,FTP实现共享文件附Vim脚本游戏

    先说个事情:我周末是不更新这个系列教程的,不过其他内容的会更新,我周末就整理这一周的各种内容到我的微信公众号中,提供给大家! 期待已久的linux运维.oracle"培训班"终于开 ...

  6. C# 序列化与反序列化

    我们先说说什么是序列化.所谓的序列化就是把要保存的内容以特定的格式存入某种介质中.比如我们要保存一些数据在下次程序启动的时候再读入.我们一般就是把这个数据写在一个本地的文本中.然后程序启动的时候去读入 ...

  7. PKI系统深入介绍

    公钥基础设施(Public Key Infrastructure,简称PKI)是目前网络安全建设的基础与核心,是电子商务安全实施的基本保障,因 此,对PKI技术的研究和开发成为目前信息安全领域的热点. ...

  8. MATLAB中plot()画图的颜色线型和希腊字母参数设置

    y         黄色           ·             点线      m         粉红           ○             圈线      c          ...

  9. Mysql 慢查询和慢查询日志分析

    众所周知,大访问量的情况下,可添加节点或改变架构可有效的缓解数据库压力,不过一切的原点,都是从单台mysql开始的.下面总结一些使用过或者研究过的经验,从配置以及调节索引的方面入手,对mysql进行一 ...

  10. [转]session 持久化问题(重启服务器session 仍然存在)

    转:http://xiaolongfeixiang.iteye.com/blog/560800 关于在线人数统计,大都使用SessionListener监听器实现. SessionListener 触 ...