BZOJ_P1176 [Balkan2007]Mokia(CDQ分治+树状数组)

10 篇文章 0 订阅
3 篇文章 0 订阅

BZOJ传送门

Time Limit: 30 Sec Memory Limit: 162 MB
Submit: 1605 Solved: 697
[Submit][Status][Discuss]
Description
维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000.

Input

第一行两个整数,S,W;其中S为矩阵初始值;W为矩阵大小
接下来每行为一下三种输入之一(不包含引号):
“1 x y a”
“2 x1 y1 x2 y2”
“3”
输入1:你需要把(x,y)(第x行第y列)的格子权值增加a
输入2:你需要求出以左上角为(x1,y1),右下角为(x2,y2)的矩阵内所有格子的权值和,并输出
输入3:表示输入结束

Output

对于每个输入2,输出一行,即输入2的答案

Sample Input

0 4
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3

Sample Output

3
5

HINT

保证答案不会超过int范围

Source

Sol:
体会到了CDQ分治的强大,强行把复杂度降到log
也是一道论文题,用到了上一篇博文打的离散化树状数组,建议做这题前做一下传送门

离散化x,递归处理左子树,在右子树中加入左子树的贡献,清空左子树做出的贡献,直接memset不太好就改造一下树状数组,写一个标记即可。

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
#define N 200005
#define W 2000005
inline int in(int x=0,int v=1,char ch=getchar()){while(ch!='-'&&(ch>'9'||ch<'0')) ch=getchar();
    if(ch=='-') v=-1,ch=getchar();while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*v;}
struct Point{int x,y,k,v,f,s,id;}q[N],tmp[N];
int s,w,cnt,tot,deep;int ans[N];int d[W],g[W];
inline bool operator < (const Point &a,const Point &b){return a.x<b.x||(a.x==b.x&&a.id<b.id);}
inline void Add(int x,int v){for(;x<=w;x+=x&-x){if(g[x]!=deep) g[x]=deep,d[x]=0;d[x]+=v;}}
inline int Sum(int x,int res=0){for(;x;x-=x&-x) if(g[x]==deep) res+=d[x];return res;}
void Solve(int l,int r){
    if(l==r) return;
    int mid=(l+r)>>1,h=l,t=mid+1;
    for(int i=l;i<=r;i++) if(q[i].id<=mid) tmp[h++]=q[i];else tmp[t++]=q[i];
    for(int i=l;i<=r;i++) q[i]=tmp[i];
    Solve(l,mid);deep++;
    for(int i=mid+1,j=l;i<=r;i++){
        for(;j<=mid&&q[j].x<=q[i].x;j++) if(q[j].k==1) Add(q[j].y,q[j].v);
        if(q[i].k==2) ans[q[i].s]+=Sum(q[i].y)*q[i].f;
    }
    Solve(mid+1,r);h=l,t=mid+1;
    for(int i=l;i<=r;i++){
        if(t>r||(h<=mid&&q[h].x<=q[t].x)) tmp[i]=q[h++];
        else tmp[i]=q[t++];
    }
    for(int i=l;i<=r;i++) q[i]=tmp[i];
}
int main(){
    s=in(),w=in();int k,a,b,x,y;
    for(;;){
        k=in();if(k==3) break;
        if(k==1) x=in(),y=in(),a=in(),q[++tot]=(Point){x,y,k,a,0,0,tot};
        if(k==2)
            a=in(),b=in(),x=in(),y=in(),
            q[++tot]=(Point){a-1,b-1,k,0,1,++cnt,tot},
            q[++tot]=(Point){a-1,y,k,0,-1,cnt,tot},
            q[++tot]=(Point){x,b-1,k,0,-1,cnt,tot},
            q[++tot]=(Point){x,y,k,0,1,cnt,tot},
            ans[cnt]+=(x-a+1)*(y-b+1)*s;
    }
    sort(q+1,q+tot+1);Solve(1,tot);
    for(int i=1;i<=cnt;i++) printf("%d\n",ans[i]);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值