1 条题解
-
0
自动搬运
来自洛谷,原作者为

George1123
**搬运于
2025-08-24 22:17:07,当前版本为作者最后更新于2020-02-09 20:58:00,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
blog上的题解地址:题解-[MdOI2020] Decrease
[MdOI2020] Decrease
今天巨佬团队 公开赛中的第三题,当时我写了好久才想到暴力做法 ,后来我还很离谱的写了个二维线段树,最终也没做出来。看来我还是太蒻了。
其实此题的做法是:简单差分
审题很重要,按照题目描述输入矩阵,题目中也说了,要快读:
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; }这么蒟蒻的做法,我拿到了 ,可见暴力的重要性。
然后我们发现,修改的区间一定是一个矩形,而且是增减修改,并且是统一修改,就应该想到用差分。把每行单独拎出来差分一下,形成差分数组 :
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; //k*k矩阵修改通过差分优化,时间复杂度为O(n) } }因为要覆盖的左端点少,所以这样的代码时间复杂度是合理的。
题目中说还有“无法使矩阵中所有数都变为 ”的情况,所以最后再 暴力扫一遍差分矩阵,如果还有没清零的数,就 。代码:
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
- 上传者