zoukankan      html  css  js  c++  java
  • 题解-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;
    }
    

    祝大家学习愉快!

  • 相关阅读:
    Scala 中 for 循环 和 generator 的使用例子
    [转] tomcat进程意外退出的问题分析
    [转] Android:用GSON 五招之内搞定任何JSON数组
    [转] Scala 2.10.0 新特性之字符串插值
    [转] JQuery UI Tabs 动态添加页签,并跳转到新页签
    vim常用快捷键
    [转] 利用dockerize模板为容器内应用生成配置文件和环境变量
    [转] linux权限补充:rwt rwT rws rwS 特殊权限
    [转] #!/bin/sh & #!/bin/bash区别
    [转] 利用shell创建文本菜单与窗口部件的方法
  • 原文地址:https://www.cnblogs.com/George1123/p/12444590.html
Copyright © 2011-2022 走看看