1 条题解

  • 0
    @ 2025-8-24 22:17:07

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar George1123
    **

    搬运于2025-08-24 22:17:07,当前版本为作者最后更新于2020-02-09 20:58:00,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    blog上的题解地址:题解-[MdOI2020] Decrease

    [MdOI2020] Decrease

    今天巨佬团队 luogu\texttt{luogu} 公开赛中的第三题,当时我写了好久才想到暴力做法 42分\color{orange}\texttt{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\n",ans);
    	return 0;
    }
    

    这么蒟蒻的做法,我拿到了 42分\color{orange}\texttt{42分},可见暴力的重要性。

    然后我们发现,修改的区间一定是一个矩形,而且是增减修改,并且是统一修改,就应该想到用差分。把每行单独拎出来差分一下,形成差分数组 cf[][]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×kk\times k 矩阵减去左上角端点的数,然后让 ans\texttt{ans} 加上左上角端点数的绝对值。因为当枚举到一个端点的时候,它同一行左端的端点肯定被清零了,所以到这个端点时这个端点的值就是 cf[][]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)
    		}
    	}
    

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

    题目中说还有“无法使矩阵中所有数都变为 00”的情况,所以最后再 n×nn\times n 暴力扫一遍差分矩阵,如果还有没清零的数,就 puts("-1")\texttt{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\n",ans);
    	return 0;
    }
    

    祝大家学习愉快!

    • 1

    信息

    ID
    5052
    时间
    1000ms
    内存
    500MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者