2018牛客暑假多校二 J(二维树状数组)

题目意思:

    有一个n*m的矩形,每个位置有一个数。有T次操作,每次往一个子矩形的每个格子中放入一个数。 求有多少个格子中被放入了至少一个与对应位置不相同的数。 n*m<=1e6,T<=1e6。

题目分析:

    对于这个题,因为我们需要处理的是一个矩形区域的问题,因为我们不难想到可以用二维线段树/二维树状数组去做。(但是鉴于线段树常数过大,而这个题还是挺卡时间的,因此最好采用二维树状数组进行解决)。

    我们可以考虑这样的一个做法:我们用一个vector存储某一种药可以作用在哪一些点上;再用一个vector存储T种方案中,每一个种类的药所涉及的范围。假设某一点没有被药所覆盖,该点为0,否则该点大于0。则我们在每一种方案浇花的时,我们用二位树状数组在[x1,y1],[x2,y2]这样的二维空间加1,代表被一种药浇到了。

    最后就是整个做法的精髓:我们遍历所有的药的种类,先判断是否这种药是否可以作用在某一点上,如果可行,就遍历所有的有该药的花的区间,消除它的影响。然后再遍历所有的以这种药为生的花,判断是否会死(此时如果遍历的结果发现该花所在的区间不为0,即有其他的药作用在这个花的区间了,则代表花必死)。

    这样不断的遍历即可获得答案。

    具体细节在代码中有所标记。

代码:

#include <bits/stdc++.h>
#define maxn 1000005
using namespace std;
typedef long long ll;
typedef pair<int,int>PLL;
vector<int>bit[maxn];//vector bit是二维树状数组
vector<PLL>a[maxn];//vector a存储的是第i种药所对应的坐标
struct node{
    int a,b,c,d;
    node(int _a,int _b,int _c,int _d){
        a=_a,b=_b,c=_c,d=_d;
    }
};
vector<node>q[maxn];//vector q存储的是第i种药品所撒的区间
int n,m,t;
int lowbit(int x){
    return x&-x;
}
void add(int x,int y,int z){
    for(int i=x;i<=n;i+=lowbit(i)){
        for(int j=y;j<=m;j+=lowbit(j))
            bit[i][j]+=z;
    }
}
void update(int x1,int y1,int x2,int y2,int z){
    add(x1,y1,z);
    add(x2+1,y2+1,z);
    add(x1,y2+1,-z);
    add(x2+1,y1,-z);
}
int query(int x,int y){
    int res=0;
    for(int i=x;i;i-=lowbit(i)){
        for(int j=y;j;j-=lowbit(j)){
            res+=bit[i][j];
        }
    }
    return res;
}
unsigned int read()//读入挂
{
    unsigned int ret=0;
    char ch=getchar();
    while(ch>'9'||ch<'0')ch=getchar();
    while(ch>='0'&&ch<='9')
    {
        ret=ret*10+ch-'0';
        ch=getchar();
    }
    return ret;
}
int main()
{
    scanf("%d%d%d",&n,&m,&t);
    for(int i=1;i<=n;i++) bit[i].resize(m+1);//预开空间
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            int z;
            z=read();
            //scanf("%d",&z);
            a[z].push_back(make_pair(i,j));
        }
    }
    for(int i=1;i<=t;i++){
        int a,b,c,d,k;
        a=read(),b=read(),c=read(),d=read(),k=read();
        //scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        update(a,b,c,d,1);//将这一片区间置1
        q[k].push_back(node(a,b,c,d));
    }
    int ans=0;
    for(int i=1;i<=n*m;i++){//枚举每一种药
        if(a[i].size()){//如果有某种花许需要第i种药
            for(auto nw:q[i])//遍历所有的有该药的区间,消除它的影响
                update(nw.a,nw.b,nw.c,nw.d,-1);
            for(auto nw:a[i]){//遍历所有的以这种药为生的花,判断是否会作用
                if(query(nw.first,nw.second)) ans++;
            }
            for(auto nw:q[i])//回复这种药的作用。
                update(nw.a,nw.b,nw.c,nw.d,1);
        }
    }
    cout<<ans<<endl;//输出结果!
    return 0;
}
原文地址:https://www.cnblogs.com/Chen-Jr/p/11007264.html