P4514 上帝造题的七分钟

传送门

看到题目一眼 $cdq$ 分治,然后发现 $n,m$ 很小,感到一丝不对劲

然后去看看题解发现正解是二维树状数组

二维树状数组和一维的好像也差不多?

struct BIT {
    int t[N][N];
    inline void add(int x,int y,int v)
    {
        for(;x<=n;x+=x&-x)
            for(int j=y;j<=m;j+=j&-j) t[x][j]+=v;
    }
    inline int ask(int x,int y)
    {
        int res=0;
        for(;x;x-=x&-x)
            for(int j=y;j;j-=j&-j) res+=t[x][j];
        return res;
    }
};
二维BIT

题目要求的是矩形修改矩形询问,考虑差分并用差分数组求原数组二维前缀和再容斥一下,设差分矩阵为 $A$

那么对于一个二维前缀和的询问 $(x,y)$ ,差分矩阵某个位置 $(a,b),a \leq x,b \leq y$ 被统计当且仅当 $x' \leq x,y' \leq y$ 并且 $a \leq x' ,b \leq y'$ 

显然这样的 $(x',y')$ 有 $(x-a+1)*(y-b+1)$ 对,那么这个位置 $(a,b)$ 就会被统计到 $(x-a+1)*(y-b+1)$ 次,所以贡献为 $A[a][b]*(x-a+1)*(y-b+1)$

把后面拆开 $(xy-xb+x-ay+ab-a+y-b+1)=(xy+x+y+1)-(x+1)b-(y+1)a+ab$

所以对于二维前缀和的询问 $(x,y)$ 我们只要开四个树状数组分别维护 $A[a][b],A[a][b]*b,A[a][b]*a,A[a][b]*ab$ 即可

最后容斥一下就是答案了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
inline 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<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=2049,M=2e5+7;
int n,m;
struct BIT {
    int t[N][N];
    inline void add(int x,int y,int v)
    {
        for(;x<=n;x+=x&-x)
            for(int j=y;j<=m;j+=j&-j) t[x][j]+=v;
    }
    inline int ask(int x,int y)
    {
        int res=0;
        for(;x;x-=x&-x)
            for(int j=y;j;j-=j&-j) res+=t[x][j];
        return res;
    }
}T,Ti,Tj,Tij;
inline void ADD(int x,int y,int v)
{
    T.add(x,y,v); Ti.add(x,y,v*x);
    Tj.add(x,y,v*y); Tij.add(x,y,v*x*y);
}
inline int ASK(int x,int y)
{
    return T.ask(x,y)*(x*y+x+y+1)+Tij.ask(x,y)
            -Ti.ask(x,y)*(y+1)-Tj.ask(x,y)*(x+1);
}
int main()
{
    char s[7]; scanf("%s",s); n=read(),m=read();
    int a,b,c,d,v;
    while(scanf("%s",s)!=EOF)
    {
        a=read(),b=read(),c=read(),d=read();
        if(s[0]=='L')
        {
            v=read(); ADD(a,b,v); ADD(c+1,d+1,v);
            ADD(a,d+1,-v); ADD(c+1,b,-v);
        }
        else printf("%d\n",ASK(a-1,b-1)+ASK(c,d)-ASK(a-1,d)-ASK(c,b-1));
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/LLTYYC/p/11505738.html