• 题解-Decrease


    [MdOI2020] Decrease

    古老的博文。

    今天巨佬团队 ( exttt{luogu}) 公开赛中的第三题,当时我写了好久才想到暴力做法 ( exttt{42分}),后来我还很离谱的写了个二维线段树,最终也没做出来。看来我还是太蒻了。

    其实此题的做法是:简单差分

    审题很重要,按照题目描述输入矩阵,题目中也说了,要快读:

    for(int i=1,x,y,z;i<=m;i++){
    	x=d(),y=d(),z=d();
    	a[x][y]=z;
    }
    

    暴力做法:枚举覆盖正方形的左上角,暴力覆盖。 代码:

    int main(){
    	n=d(),m=d(),k=d();
    	for(int i=1,x,y,z;i<=m;i++){
    		x=d(),y=d(),z=d();
    		a[x][y]=z;
    	}
    	for(int i=1;i<=n-k+1;i++)
    		for(int j=1;j<=n-k+1;j++)
    			if(a[i][j]!=0){
    				ans+=abs(a[i][j]);
    				int tmp=a[i][j];
    				for(int x=i;x<=i+k-1;x++)
    					for(int y=j;y<=j+k-1;y++)
    						a[x][y]-=tmp;
    			}
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++)
    			if(a[i][j]!=0) return puts("-1"),0;
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    这么蒟蒻的做法,我拿到了 ( exttt{42分}),可见暴力的重要性。

    然后我们发现,修改的区间一定是一个矩形,而且是增减修改,并且是统一修改,就应该想到用差分。把每行单独拎出来差分一下,形成差分数组 (cf[][])

    for(int i=1;i<=n;i++)
    	for(int j=1;j<=n;j++)
    		cf[i][j]=a[i][j]-a[i][j-1];
    

    然后暴力枚举覆盖正方形左上角端点,如果不为零就把整个右下角的 (k imes k) 矩阵减去左上角端点的数,然后让 ( exttt{ans}) 加上左上角端点数的绝对值。因为当枚举到一个端点的时候,它同一行左端的端点肯定被清零了,所以到这个端点时这个端点的值就是 (cf[][]) 了。代码:

    for(int i=1;i<=n-k+1;i++)
    	for(int j=1,num=0;j<=n-k+1;j++){
    		num=cf[i][j];
    		if(num!=0){
    			ans+=abs(num);
    			for(int t=i;t<=i+k-1;t++)
    				cf[t][j]-=num,cf[t][j+k]+=num; //k*k矩阵修改通过差分优化,时间复杂度为O(n)
    		}
    	}
    

    因为要覆盖的左端点少,所以这样的代码时间复杂度是合理的。

    题目中说还有“无法使矩阵中所有数都变为 (0)”的情况,所以最后再 (n imes n) 暴力扫一遍差分矩阵,如果还有没清零的数,就 ( exttt{puts("-1")})。代码:

    for(int i=1;i<=n;i++)
    	for(int j=1;j<=n;j++)
    		if(cf[i][j]) return puts("-1"),0;
    

    就这么简单,普及难度,可是我比赛时缺没想到。如果你懂了,那么蒟蒻就放代码了:

    #include <bits/stdc++.h>
    using namespace std;
    #define lng long long
    namespace rd{
        const int L=1<<16;
        char buf[L],*S,*T;
        inline char Gc_(){
            if(S==T){T=(S=buf)+fread(buf,1,L,stdin);
                if(S==T) return EOF;}
            return *S++;
        }
        inline char Gc(){return getchar();}
        inline int d(){
            char c;int f=1,x;
            for(c=Gc();c>'9'||c<'0';c=Gc())
                if(c=='-') f=-1;
            for(x=0;c>='0'&&c<='9';c=Gc())
                x=(x<<1)+(x<<3)+c-'0';
            return x*f;
        }
    }using namespace rd;
    const int N=5e3+10;
    int n,m,k,a[N][N];
    lng ans;
    int cf[N][N];
    int main(){
    	n=d(),m=d(),k=d();
    	for(int i=1,x,y,z;i<=m;i++){
    		x=d(),y=d(),z=d();
    		a[x][y]=z;
    	}
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++)
    			cf[i][j]=a[i][j]-a[i][j-1];
    	for(int i=1;i<=n-k+1;i++)
    		for(int j=1,num=0;j<=n-k+1;j++){
    			num=cf[i][j];
    			if(num!=0){
    				ans+=abs(num);
    				for(int t=i;t<=i+k-1;t++)
    					cf[t][j]-=num,cf[t][j+k]+=num;
    			}
    		}
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++)
    			if(cf[i][j]) return puts("-1"),0;
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    祝大家学习愉快!

  • 相关阅读:
    自定义TabControl
    认识——转化的力量
    提取网页中的javascript脚本和下载链接
    【作业】简单绘图程序
    遥感中几个物理量计算及比较
    [C++学习]C++内存管理
    小技巧
    Threading.Timer学习
    Unsupported Oracle data type 101 encountered
    Vs2008不能调试的问题
  • 原文地址:https://www.cnblogs.com/George1123/p/12444590.html
Copyright © 2020-2023  润新知