bzoj 1176: Mokia CDQ分治

人生第一道自己写的CDQ LOL~~~

题目大意

维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000.

题解

CDQ分治裸上啊...
啥是CDQ啊?....
怎么说呢..
CDQ分治是一种思想.
将整个序列分成两半递归处理,但是由于左边对右边的序列存在着不可忽略的影响,所以需要在递归时考虑左半边序列对右半边序列的影响。
即CDQ的核心思想.(大概...)
至于具体的实现过程,这种东西还是对着代码比较好学。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
    x=0;char ch;bool flag = false;
    while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const int maxm = 210010;
const int maxn = 2100000;
struct Node{
    int op,x,y;
    int val,id;
    bool friend operator < (const Node &a,const Node &b){
        return a.x < b.x;
    }Node(){}
    Node(int a,int b,int c,int d,int e){op=a;x=b;y=c;val=d;id = e;}
}a[maxm],atmp[maxm];
int cnt,n;
int anss[10010];
int c[maxn],tmp[maxm];
#define lowbit(x) (x&-x)
inline void modify(int x,int y){
    if(!x) return;
    for(;x <= n;x+=lowbit(x)) c[x] += y;
}
inline int query(int x){
    int ret = 0;
    for(;x>=1;x-=lowbit(x)) ret += c[x];
    return ret;
}
inline void merge(int l,int r){
    if(l == r) return;
    int mid = l+r >> 1;
    int i = l,j = mid+1,k = l;
    while(i <= mid || j <= r){
        if(j > r || ( i <= mid && a[i].x < a[j].x)) atmp[k++] = a[i++];
        else atmp[k++] = a[j++];
    }for(int i=l;i<=r;++i) a[i] = atmp[i];
}
void solve(int l,int r){//CDQ的分治
    if(l == r) return ;
    int mid = l+r >> 1;
    solve(l,mid);//递归处理子序列
    solve(mid+1,r);
    merge(l,mid);//对左右序列分别排序
    merge(mid+1,r);
    //下面开始考虑左序列(中的插入操作)对右序列(中的询问操作)的影响
    int p = l,cnt = 0;
    for(int i = mid+1;i<=r;++i){
        if(a[i].op == 2){//如果这是个询问操作
            while(p <= mid && a[p].x <= a[i].x){
                if(a[p].op == 1){//对于左序列所有的操作,更新影响
                    tmp[++cnt] = p;
                    modify(a[p].y,a[p].val);
                }++p;
            }anss[a[i].id] += a[i].val*query(a[i].y);//做出贡献
        }
    }
    //清空树状数组,注意不能memset
    for(int i=1;i<=cnt;++i) modify(a[tmp[i]].y,-a[tmp[i]].val);
}
int main(){
    read(cnt);read(n);
    int op,x1,y1,x2,y2,x;
    int query_id = 0;
    while(1){
        read(op);if(op == 3) break;
        if(op == 1){
            read(x1);read(y1);read(x);
            a[++cnt] = (Node){op,x1,y1,x,0};
        }else{
            x = ++query_id;
            read(x1);read(y1);read(x2);read(y2);
            //将所有的询问做成前缀和差分的形式
            a[++cnt] = (Node){op,x2,y2,1,x};
            a[++cnt] = (Node){op,x1-1,y2,-1,x};
            a[++cnt] = (Node){op,x2,y1-1,-1,x};
            a[++cnt] = (Node){op,x1-1,y1-1,1,x};
        }
    }
    solve(1,cnt);
    for(int i=1;i<=query_id;++i){
        printf("%d\n",anss[i]);
    }
    getchar();getchar();
    return 0;
}

转载于:https://www.cnblogs.com/Skyminer/p/6403249.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值