COGS 577 蝗灾

传送门
时间限制: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
", 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
", res[i]);
}
原文地址:https://www.cnblogs.com/Patt/p/5844723.html