[学习笔记]CDQ分治 bzoj1176 [Baltic2007] Mokia

64 篇文章 0 订阅
14 篇文章 0 订阅

题目大意:支持单点修改和矩阵查询,n<=2e6,q<=2e5,允许离线。
显然不能树套树之类的,空间复杂度飞起。
学CDQ分治,过程很好理解,就是先递归处理两部分,然后合并。
思想上就是每个询问只依赖于之前的修改并且可以独立考虑修改。
即修改的贡献是互不影响的。这种情况下可以这么分治,用一个log的代价改成先给你一些修改剩下的都是询问的问题。
这个题这样转化之后就变成了经典的扫描线问题不再赘述。
代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#define lint long long
#define Q 400010
#define N 2000010
#define lb(x) (x&-x)
using namespace std;
struct queries{
    int k,w,x,y,z,id;//y<=z
    inline queries operator=(const queries &q)
    {
        k=q.k,w=q.w,x=q.x,y=q.y,z=q.z,id=q.id;return *this;
    }
}q[Q],tmp[Q];
lint b[N],ans[Q];
int cnt,updx[Q],updw[Q],n;
inline bool cmp(const queries &x,const queries &y)
{
    if(x.x^y.x) return x.x<y.x;
    else return x.k<y.k;
}
inline int update(int x,int w)
{
    cnt++,updx[cnt]=x,updw[cnt]=w;
    for(;x<=n;x+=lb(x)) b[x]+=w;
    return 0;
}
inline lint query(int s,int t)
{
    lint ans=0;
    for(;t;t-=lb(t)) ans+=b[t];
    for(s--;s;s-=lb(s)) ans-=b[s];
    return ans;
}
int solve(int l,int r)
{
    if(l==r) return 0;int mid=(l+r)>>1,x,y;
    solve(l,mid),solve(mid+1,r),x=l,cnt=0;
    for(int y=mid+1;y<=r;y++)
    {
        if(q[y].k==1) continue;
        while(x<=mid&&q[x].x<=q[y].x)
        {
            while(x<=mid&&q[x].x<=q[y].x&&q[x].k==2) x++;
            if(x<=mid&&q[x].x<=q[y].x) update(q[x].y,q[x].w),x++;
        }
        ans[q[y].id]+=q[y].w*query(q[y].y,q[y].z);
    }
    for(int i=1;i<=cnt;i++) 
        update(updx[i],-updw[i]),cnt--;
    x=l,y=mid+1;int t=0;
    while(x<=mid&&y<=r)
        if(cmp(q[x],q[y])) tmp[++t]=q[x++];
        else tmp[++t]=q[y++];
    while(x<=mid) tmp[++t]=q[x++];
    while(y<=r) tmp[++t]=q[y++];
    for(int i=1,j=l;i<=t;q[j++]=tmp[i++]);
    return 0;
}
int main()
{
    int s,opt,qcnt=0,m=0;scanf("%d%d",&s,&n);
    while(scanf("%d",&opt)!=EOF&&opt!=3)
        if(opt==1) q[++m].k=1,scanf("%d%d%d",&q[m].x,&q[m].y,&q[m].w);
        else{
            int x1,y1,x2,y2;scanf("%d%d%d%d",&x1,&y1,&x2,&y2),qcnt++;
            q[++m].k=2,q[m].x=x1-1,q[m].y=y1,q[m].z=y2,q[m].w=-1,q[m].id=qcnt;
            q[++m].k=2,q[m].x=x2,q[m].y=y1,q[m].z=y2,q[m].w=1,q[m].id=qcnt;
            ans[qcnt]=(lint)s*(y2-y1+1)*(x2-x1+1);
        }
    solve(1,m);
    for(int i=1;i<=qcnt;i++)
        printf("%lld\n",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值