BZOJ1176: [Balkan2007]Mokia

BZOJ1176

算得上是CDQ分治三维偏序的模板题了吧QAQ。
我们就把查询操作容斥一下,就变成了求

sum(1,1,x2,y2)sum(1,1,x11,y2)sum(1,1,x2,y11)+sum(1,1,x11,y11)

那么相当于我们只需要维护一个二维的前缀和。
定义三元组 t,x,y t表示时间,x,y分别表示一维。
那么就是要求 (tt,xx,yy) 满足 tt<t,xx<=x,yy<=y 的和。
t是默认有序的,然后以X坐标为序,y用树状数组维护即可。
具体实现中我用了五个值表示状态:
type(0/1) 表示是修改还是查询, x,y 表示两个维度。 w 在修改中表示要增加的值,在查询中(1/1)表示是 (+/) aid 表示是第几个查询。

Warning!
1.对于相同的 x <script type="math/tex" id="MathJax-Element-12">x</script>,要先修改再查询。
2.每一次操作要将树状数组清空。

【代码】

#include <cstdio>
#include <iostream>
#include <algorithm>
#define N 200005
#define INF 0x7fffffff
using namespace std;
typedef long long ll;

ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}

int n,m,cnt,tot,na; 
int ans[10005],szsz[2000005];

class Ope{
    public:
        int type,id,num,x,y,w;
    Ope(){}
    Ope(int  tt,int ii,int nn,int xx,int yy,int ww) {
        type=tt,id=ii,num=nn,x=xx,y=yy,w=ww;
    }
}e[N],tmp[N];

bool operator <(Ope A,Ope B) {
    return A.x<B.x||(A.x==B.x&&A.type<B.type);
}

int lowbit(int x) {
    return x&-x;
}

void Update(int x,int y) {
    for(int i=x;i<=m;i+=lowbit(i)) szsz[i]+=y;
}

int Query(int x) {
    int rtn=0;
    for(int i=x;i;i-=lowbit(i)) rtn+=szsz[i];
    return rtn;
}

void Clear(int x){
    for(int i=x;i<=m&&szsz[i];i+=lowbit(i)) szsz[i]=0;
}

void CDQ(int l,int r)
{
    if(l==r) return;
    int mid=l+r>>1;
    CDQ(l,mid);CDQ(mid+1,r);
    int p=l,q=mid+1,o=0;
    while(p<=mid&&q<=r)
    {
        if(e[p]<e[q]) {
            if(!e[p].type)
                Update(e[p].y,e[p].w);
            tmp[o++]=e[p++];
        }
        else 
        {
            if(e[q].type)
                ans[e[q].num]+=e[q].w*Query(e[q].y);
            tmp[o++]=e[q++];    
        }
    }
    while(p<=mid) {
        if(!e[p].type)
            Update(e[p].y,e[p].w);
        tmp[o++]=e[p++];
    }
    while(q<=r) {
        if(e[q].type) ans[e[q].num]+=e[q].w*Query(e[q].y);
        tmp[o++]=e[q++];
    }
    for(int i=0;i<o;i++) {
        if(!tmp[i].type&&tmp[i].id<=mid) 
            Update(tmp[i].y,-tmp[i].w);
        e[l+i]=tmp[i];
    }
}

int main()
{
    n=read(),m=read();
    while(1)
    {
        static int f,x,y,xx,yy;
        f=read();if(f==3) break;
        else if(f==1) {
            x=read(),y=read(),xx=read();
            e[++tot]=Ope(0,tot,0,x,y,xx);
        }
        else {
            x=read(),y=read(),xx=read(),yy=read();
            e[++tot]=Ope(1,tot,++na,x-1,y-1,1);
            e[++tot]=Ope(1,tot,na,x-1,yy,-1);
            e[++tot]=Ope(1,tot,na,xx,y-1,-1);
            e[++tot]=Ope(1,tot,na,xx,yy,1);
        }
    }
    CDQ(1,tot);
    for(int i=1;i<=na;i++) printf("%d\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值