• 2018牛客网暑期ACM多校训练营(第二场)J Farm(树状数组)


    题意

    n*m的农场有若干种不同种类作物,如果作物接受了不同种类的肥料就会枯萎。现在进行t次施肥,每次对一个矩形区域施某种类的肥料。问最后枯萎的作物是多少。

    分析

    作者:xseventh
    链接:https://www.nowcoder.com/discuss/87630?type=101
    来源:牛客网

    我们通过差分(其实就是二维前缀和)可以在O(nm)的时间求出每个点被撒了几次肥料
    然后我们可以对每种植物分开求被撒了几次相同种类的肥料,然后判断被撒肥料的次数是否等于被撒的相同种类肥料的次数来确定这个植物的死活
    首先  我们需要学会二维差分,这个怎么做呢?我们发现如果我们想对(x1,y1) (x2,y2)这个矩形内所有元素+1
    我们可以通过a[x1][y1]++,a[x1][y2+1]--,a[x2+1][y1]--,a[x2+1][y2+1]++这四个操作来实现
    我们对这四个点进行加减后,再对矩阵求一次二维前缀和,这时候的效果就是整个矩形+1了

    然后我们开始考虑如何求每种的植物被撒了几次肥料。
    我们要分别求n*m种植物被撒了几次肥料,所以我们肯定不能每一次都通过二维前缀和来维护被撒了几次肥料。
    这时候我们发现这其实是个二维偏序的问题,我们对每种植物的贡献排序,然后用树状数组维护一下就好了。众所周知 二维偏序的做法复杂度是一个log的,我们一共有n*m次操作,所以总复杂度为O(n*m*log(n*m))
    具体怎么维护呢,我们通过对x和y坐标排序,按照顺序把贡献加到树状数组里,树状数组里前面的元素的x坐标一定是小于等于当前元素的,我们只需要求树状数组内<=y的和就能统计出x,y都<=y的元素和
    注意一点,当x和y相等的时候,询问一定要在修改后面,排序时候判断一下就行了。
    注意 清空树状数组的时候为了保证复杂度,我们只能重新做一次之前的修改,把负的权值加上去。
    具体代码如下
    #include <bits/stdc++.h>
    
    using namespace std;
    int c[5100010];
    int n,m;
    int id(int i,int j){
        return i*(m+2)+j;
    }
    
    struct nod{
        int x,y,val;
        bool operator < (const nod &b)const{
            if(x==b.x&&y==b.y){
                return abs(val)>abs(b.val);
            }
            if(x==b.x)
                return y<b.y;
            return x<b.x;
        }
    };
    vector<nod>vd[1100010];
    int bit[1100010];
    void add(int pos,int x){
        while(pos<=m+1){
            bit[pos]+=x;
            pos+=pos&-pos;
        }
    }
    int sum(int pos){
        int Sum=0;
        while(pos){
            Sum+=bit[pos];
            pos-=pos&-pos;
        }
        return Sum;
    }
    int main(){
    #ifdef LOCAL
        freopen("in.txt","r",stdin);
    #endif // LOCAL
        int t,x;
        scanf("%d%d%d",&n,&m,&t);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                scanf("%d",&x);
                vd[x].push_back(nod{i,j,0});
            }
        int x1,x2,y1,y2;
        while(t--){
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            int k;
            scanf("%d",&k);
            vd[k].push_back(nod{x1,y1,1});
            vd[k].push_back(nod{x2+1,y2+1,1});
            vd[k].push_back(nod{x1,y2+1,-1});
            vd[k].push_back(nod{x2+1,y1,-1});
            c[id(x1,y1)]++;
            c[id(x2+1,y2+1)]++;
            c[id(x1,y2+1)]--;
            c[id(x2+1,y1)]--;
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                c[id(i,j)]+=c[id(i-1,j)]+c[id(i,j-1)]-c[id(i-1,j-1)];//被撒肥料总次数
            }
        }
        int ans=0;
        for(int i=1;i<=n*m;i++){
            sort(vd[i].begin(),vd[i].end());
            for(int j=0;j<vd[i].size();j++){
                if(vd[i][j].val==0){
                    if(c[id(vd[i][j].x,vd[i][j].y)]==sum(vd[i][j].y))
                        ans++;
                }
                else {
                    add(vd[i][j].y,vd[i][j].val);
                }
            }
            for(int j=0;j<vd[i].size();j++)
                if(vd[i][j].val)
                    add(vd[i][j].y,-vd[i][j].val);
        }
        printf("%d
    ",n*m-ans);
        return 0;
    }
  • 相关阅读:
    SpringBoot之Banner介绍
    SpringBoot事件监听机制
    SpringBoot 启动流程图
    ApplicationContextInitializer的理解和使用
    SpringFactoriesLoader解析
    计时器之StopWatch
    ftp上下载文件
    打印两个函数的返回值
    关闭所有已打开的文件和关闭应用
    TypeError: include() got an unexpected keyword argument 'app_name'
  • 原文地址:https://www.cnblogs.com/fht-litost/p/9356910.html
Copyright © 2020-2023  润新知