【Balkan2007】Mokia

【Balkan2007】Mokia

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范围

Solution

这道题的数据范围十分巨大,以至于各种各样的暴力数据结构都挂了。。。

我们需要学习一种新的方法来解决这一类问题,那就是神奇的CDQ分治。

这是一种离线的询问方法,它将修改操作和询问操作一视同仁,将它们按照操作时间进行排序。接着就是分治的步骤了。

我们将原有的询问分为左右两个集合,分别处理并记下答案。但是在这一操作之前,我们还需要让左集合的事件对右集合的询问产生影响,然后总和起来就得到了每一个询问的答案。

在这一道题中,我们知道可以用前缀和来得出一个子矩阵的总和,所以我们用CDQ分治x轴,树状数组维护y轴。

CODE

#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
namespace Istream {
    const int L=1<<15;
    char buffer[L],*Z,*T;
    inline char getchar() {
        if(Z==T) {
            T=(Z=buffer)+fread(buffer,1,L,stdin);
            if(Z==T)return EOF;
        }
        return *Z++;
    }
    inline int read() {
        char c;
        int rec=0,f=1;
        for(c=getchar(); c<'0'||c>'9'; c=getchar())if(c=='-')f=-1;
        while(c>='0'&&c<='9')rec=(rec<<1)+(rec<<3)+(c-'0'),c=getchar();
        return rec*f;
    }
}
using namespace Istream;//一次性将缓冲区的全部文件读入,然后处理。只能用于文件输入输出。 
const int N=500010;
const int M=2000100;
struct node {
    int op,x,y,z,t,id;
} p[N],s[N];
int S,n,num,tot,id[N];
int h[M],ans[N];
inline bool cmp(const node &a,const node &b){return a.x<b.x;}//排序 
void addquery(int i,int op,int x,int y,int z,int id) {
    p[i].id=i;p[i].op=op;p[i].x=x;p[i].y=y;p[i].z=z;p[i].t=id;return ;
}//添加事件 
void add(int pos,int x) {while (pos<=n) h[pos]+=x,pos+=(pos&(-pos));return ;}
long long sum(int pos) {long long t=0;while (pos>0) t+=h[pos],pos-=(pos&(-pos));return t;}
//简单的树状数组 
void CDQ(int l,int r) {
    if(l==r)return;
    int mid=(l+r)>>1,i,t1=l-1,t2=mid,t=l;
    for(i=l; i<=r; i++)
        if(p[i].id<=mid)s[++t1]=p[i];
        else s[++t2]=p[i];//在时间有序的情况下再按照已有的x排序进行排序 
    memcpy(p+l,s+l,sizeof(node)*(r-l+1)); 
    for(i=mid+1; i<=r; i++)
        if(p[i].op==2) {//将该询问之前的所有修改加入树状数组。 
            for(; t<=mid&&p[t].x<=p[i].x; t++)if(p[t].op==1)add(p[t].y,p[t].z);
            ans[p[i].t]+=sum(p[i].y)*p[i].z;//确定加减 
        }
    for(i=l; i<t; i++)if(p[i].op==1)add(p[i].y,-p[i].z);//退回发生的影响,方便接下来的分治。 
    CDQ(l,mid);CDQ(mid+1,r);//分治 
    return ;
}
int ttt[500010];//负责计算出每一个询问的基础值 
int main() {
    int i,k,x,y,z,w;
    S=read();n=read();
    while (1) {
        k=read();
        if (k==3) break;
        if (k==1) {
		    x=read();y=read();z=read();
            addquery(++tot,1,x,y,z,0);
        } 
		else {
            x=read();y=read();z=read();w=read();num++;
            ttt[num]=(z-x+1)*(w-y+1)*S;//子矩阵大小乘以矩阵初值 
            addquery(++tot,2,z,w,1,num);addquery(++tot,2,x-1,y-1,1,num);
            addquery(++tot,2,x-1,w,-1,num);addquery(++tot,2,z,y-1,-1,num);
            //将一个区间询问拆分成为四个前缀和的加减操作 
        }
    }sort(p+1,p+1+tot,cmp);
    solve(1,tot);
    for (i=1; i<=num; i++)cout<<ans[i]+ttt[i]<<'\n';//输出答案 
    return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值