传送门

时间限制: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. href的那些事

    很多网站中都会使用<a>标签和 href属性来做链接,尤其在分页显示中用得最普遍.然而很多人对href的使用却并不十分了解. 1.href="#" 这个在网页中上滚回顶 ...

  2. node 学习笔记 - Modules 模块加载系统 (1)

    本文同步自我的个人博客:http://www.52cik.com/2015/12/11/learn-node-modules-path.html 用了这么久的 require,但却没有系统的学习过 n ...

  3. Lucene.Net的服务器封装+APi组件 (开源)

    为什么要封装 真不知道用什么标题合适,我这几天在研究Lucene.Net,觉得把Lucene.Net封装为一个独立的服务器,再提供一个给客户端调用的Api组件应该是一件很意思的事,主要优势有以下: 1 ...

  4. 在线程中调用SaveFileDialog

    在多线程编程中,有时候可能需要在单独线程中执行某些操作.例如,调用SaveFileDialog类保存文件.首先,我们在Main方法中创建了一个新线程,并将其指向要执行的委托SaveFileAsyn.在 ...

  5. 转载:SQL 递归树 子父节点相互查询

    if object_id('[tb]') is not null drop table [tb] go create table [tb]([modeid] int,modename varchar( ...

  6. c++ iterator(迭代器)分类及其使用

    前言: 以下的内容为我阅读c++沉思录18,19,20章的笔记以及自己的想法. 正文: 总所周知,c++的stl中提出了iterator的概念,这是C所没有的.在一般的使用中,iterator的行为很 ...

  7. C# 多线程防止卡死

    软件界面的响应特性是判断一款软件的非常重要的方面.一般来说,不管你软件功能做得有多么奇妙,如果软件有一点点死机的感觉都会让用户感到很讨厌,甚至怀疑你软件里是否藏有更大的问题. 要提高界面的响应特性,最 ...

  8. PHP 文件创建/写入

    <?php /* PHP 文件创建/写入 fopen() 函数也用于创建文件.也许有点混乱, 但是在 PHP 中,创建文件所用的函数与打开文件的相同. 如果您用 fopen() 打开并不存在的文 ...

  9. SharePoint 2013 安装图解

    转自: http://www.cnblogs.com/jianyus/archive/2013/02/01/2889653.html 介绍:文章就是SharePoint2013安装过程的图解,包括步骤 ...

  10. 【hihoCoder 1036】Trie图

    看了一下简单的$Trie图$,调模板调啊调一连调了$2h$,最后发现$-'a'$打成$-'A'$了hhh,有种摔键盘的冲动. $Trie图$是$Trie树$上建立“前缀边”,不用再像在$Trie树$上 ...