BZOJ 1176 Mokia(CDQ分治)

题目链接:BZOJ 1176

题目大意:维护一个W*W的矩阵,初始值均为S。每次操作可以增加某格子的权值或询问某子矩阵的总权值(修改操作数M<=160000,询问数Q<=10000,W<=2000000)。

题解:CDQ分治。查询操作可以分成4个(1,1)到(x,y)的子矩形的权值和查询,再加加减减。先把操作按x坐标为第一关键字,y为第二关键字排序,然后按操作的先后分治。分治的每一层里,计算左边的修改对右边的查询造成的影响,由于保证了右边询问的x坐标不小于左边修改的x坐标,用一个下标为y坐标的树状数组统计就好了,然后分治下去。

code(没有权限,并不保证AC TAT)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define maxn 2000005
#define maxm 640005
#define maxq 10005
#define lowbit(x) ((x)&(-x))
inline int read()
{
    char c=getchar(); int num=0,f=1;
    while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
    while (c<='9'&&c>='0') { num=num*10+c-'0'; c=getchar(); }
    return num*f;
}
int n,bit[maxn],ans[maxq];
struct data{
    int opt,id,qid,x,y,c;
    data(int _opt=0,int _id=0,int _qid=0,int _x=0,int _y=0,int _c=0)
    {
        opt=_opt; id=_id; qid=_qid;
        x=_x; y=_y; c=_c;
    }
}c[maxm],tmp[maxm];
inline void add(int pos,int val)
{
    for (;pos<=n;pos+=lowbit(pos)) bit[pos]+=val;
}
inline int sum(int pos)
{
    int ret=0;
    for (;pos;pos-=lowbit(pos)) ret+=bit[pos];
    return ret;
}
inline int cmp(data a,data b)
{
    return (a.x!=b.x)?(a.x<b.x):((a.y!=b.y)?(a.y<b.y):(a.opt<b.opt));
}
void cdq(int l,int r)
{
    if (l==r) return;
    int mid=l+r>>1;
    for (int i=l;i<=r;i++)
    {
        if (c[i].opt==1&&c[i].id<=mid) 
         add(c[i].y,c[i].c);
        else if (c[i].opt==2&&c[i].id>mid)
         ans[c[i].qid]+=c[i].c*sum(c[i].y);
    }
    for (int i=l;i<=r;i++) //清空树状数组,即保证了每次只统计当前区间前一半修改对后一半询问的影响
     if (c[i].opt==1&&c[i].id<=mid) add(c[i].y,-c[i].c);
    int h1=l,h2=mid;
    for (int i=l;i<=r;i++)   // 保证分治到每一层时所有操作是x坐标有序的
     if (c[i].id<=mid) tmp[h1++]=c[i];
      else tmp[++h2]=c[i];
    for (int i=l;i<=r;i++) c[i]=tmp[i];
    cdq(l,mid); cdq(mid+1,r);
}
int main()
{
    int s=read(),n=read(),tot=0,id=0;
    while (true)
    {
        int opt=read();
        if (opt==3) break;
        if (opt==1)
        {
            int x=read(),y=read(),w=read();
            tot++; data oh(1,tot,0,x,y,w);
            c[tot]=oh;
        }
        else
        {
            int x1=read(),y1=read(),x2=read(),y2=read(); id++;
            tot++; data d1(2,tot,id,x2,y2,1);
            c[tot]=d1;
            tot++; data d2(2,tot,id,x1-1,y2,-1);
            c[tot]=d2;
            tot++; data d3(2,tot,id,x2,y1-1,-1);
            c[tot]=d3;
            tot++; data d4(2,tot,id,x1-1,y1-1,1);
            c[tot]=d4;
            ans[id]=(y2-y1+1)*(x2-x1+1)*s;
        }
    }
    sort(c+1,c+tot+1,cmp);
    cdq(1,tot);
    for (int i=1;i<=id;i++) printf("%d\n",ans[i]);
    return 0;
}

总结:感觉做CDQ分治的题要考虑好左边对右边的影响怎么统计、每一层需不需要清空、需不需要保证有序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值