bzoj 2683: 简单题 cdq分治+树状数组

题意

要求资瓷下列操作:
1 x y z把(x,y)加上z
2 x1 y1 x2 y2求矩形x1 y1 x2 y2的权值和
n<=500000,x,y<=n,q<=200000

分析

首先可以把一个询问用容斥原理拆成四个询问,然后在cdq分治中二分一个横坐标mid,然后把操作从前往后扫一遍,若是修改操作且横坐标不大于mid则处理,若是询问操作且横坐标大于mid则处理;然后把操作按照横坐标分成左右两部分递归处理即可。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define N 500005
using namespace std;

int c[N],n,m,num,ans[N];
struct que{int x,y,z,op,id,ans,val;}q[N*4],tmp[N*4];

int read()
{
    int x=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

void ins(int x,int y)
{
    while (x<=n)
    {
        c[x]+=y;
        x+=x&(-x);
    }
}

int query(int x)
{
    int ans=0;
    while (x)
    {
        ans+=c[x];
        x-=x&(-x);
    }
    return ans;
}

void cdq(int l,int r,int L,int R)
{
    if (l>=r) return;
    if (L==R)
    {
        for (int i=l;i<=r;i++)
            if (q[i].op==1) ins(q[i].y,q[i].z);
            else q[i].ans+=query(q[i].y);
        for (int i=l;i<=r;i++)
            if (q[i].op==1) ins(q[i].y,-q[i].z);
        return;
    }
    int mid=(L+R)/2,s=0;
    for (int i=l;i<=r;i++)
        if (q[i].op==1)
        {
            if (q[i].x<=mid)
            {
                ins(q[i].y,q[i].z);
                s++;
            }
        }else
        {
            if (q[i].x>mid) q[i].ans+=query(q[i].y);
            else s++;
        }
    for (int i=l;i<=r;i++)
        if (q[i].op==1&&q[i].x<=mid) ins(q[i].y,-q[i].z);
    int i=l,j=l+s;
    for (int k=l;k<=r;k++)
        if (q[k].x<=mid) tmp[i++]=q[k];
        else tmp[j++]=q[k];
    for (int k=l;k<=r;k++)
        q[k]=tmp[k];
    cdq(l,i-1,L,mid);
    cdq(i,r,mid+1,R);
}

int main()
{
    //freopen("2683.in","r",stdin);
    //freopen("test.out","w",stdout);
    n=read();
    while (1)
    {
        int op=read();
        if (op==3) break;
        if (op==1)
        {
            int x=read(),y=read(),z=read();
            m++;
            q[m].op=op;q[m].x=x;q[m].y=y;q[m].z=z;
        }else
        {
            int x1=read(),y1=read(),x2=read(),y2=read();
            num++;
            m++;
            q[m].op=2;q[m].id=num;q[m].x=x2;q[m].y=y2;q[m].val=1;
            if (q[m].x==0) m--;
            m++;
            q[m].op=2;q[m].id=num;q[m].x=x1-1;q[m].y=y2;q[m].val=-1;
            if (q[m].x==0) m--;
            m++;
            q[m].op=2;q[m].id=num;q[m].x=x2;q[m].y=y1-1;q[m].val=-1;
            if (q[m].x==0) m--;
            m++;
            q[m].op=2;q[m].id=num;q[m].x=x1-1;q[m].y=y1-1;q[m].val=1;
            if (q[m].x==0) m--;
        }
    }
    cdq(1,m,1,n);
    for (int i=1;i<=m;i++)
        if (q[i].op==2) ans[q[i].id]+=q[i].val*q[i].ans;
    for (int i=1;i<=num;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、付费专栏及课程。

余额充值