CF1200D 【White Lines】
退役快一年了之后又打了场紧张刺激的$CF$(斜眼笑)
然后发现$D$题和题解里的大众做法不太一样 (思路清奇)
题意不再赘述,我们可以看到这个题~~好做~~在只有一次擦除机会,尝试以此为突破口解决问题
我们考虑擦除某一行(列同理),分别记录这一行最左端和最右端的黑块位置(分别记为$l,r$)
这里存在以下三种情况:
1,这一行没有黑块,这时无论在哪擦除,这一行必然全白,记录答案后不再考虑
2,这一行的最左黑块和最右黑块之间的距离$>k$(即$r-l+1>k$),这时无论在哪擦除,这一行必然不会全白,不再考虑
3,这一行最左黑块和最右黑块之间的距离$<=k$,考虑能够使得这一行全为白色的擦除位置(假设我们当前考虑的是第$i$行)
容易得出,对于擦除位置的选择
可行的行:第$i-k+1((i-k+1)+k-1=i)$行到第$i$行
可行的列:第$l-k+1((l-k+1)+k-1=l)$列到第$l$列
即如果擦除位置$(x,y)$满足$i-k+1<=x<=i$且$l-k+1<=y<=l$,这一次擦除可以使第$i$行变白
对于答案统计,自然想到二维前缀和
我们只需在位置$(i-k+1,l-k+1),(i+1,l+1)+1$,$(i-k+1,l+1),(i+1,l-k+1)-1$即可(二维差分的常规操作)
差分完了再做前缀和即可得出答案(别忘了累加情况一的答案)
$P.S.i-k+1$和$l-k+1$不要数组越界
上代码:
#include<iostream> #include<cstdio> using namespace std; const int maxn=2010; int n,k,ans[maxn][maxn],l[2][maxn],r[2][maxn],res,bs; bool exi[maxn][maxn]; int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) { string s; cin>>s; for(int j=0;j<n;j++) if(s[j]=='W') exi[i][j+1]=0; else exi[i][j+1]=1; } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) if(exi[i][j]) { l[0][i]=j; break; } for(int j=n;j;j--) if(exi[i][j]) { r[0][i]=j; break; } if(!l[0][i]) bs++; } for(int j=1;j<=n;j++) { for(int i=1;i<=n;i++) if(exi[i][j]) { l[1][j]=i; break; } for(int i=n;i;i--) if(exi[i][j]) { r[1][j]=i; break; } if(!l[1][j]) bs++; } for(int i=1;i<=n;i++) { if(l[0][i]&&r[0][i]-l[0][i]+1<=k) { int minx=max(i-k+1,1),miny=max(1,r[0][i]-k+1); ans[minx][miny]++; ans[i+1][miny]--; ans[minx][l[0][i]+1]--; ans[i+1][l[0][i]+1]++; } if(l[1][i]&&r[1][i]-l[1][i]+1<=k) { int miny=max(i-k+1,1),minx=max(1,r[1][i]-k+1); ans[minx][miny]++; ans[minx][i+1]--; ans[l[1][i]+1][miny]--; ans[l[1][i]+1][i+1]++; } } for(int i=1;i<=n-k+1;i++) for(int j=1;j<=n-k+1;j++) { ans[i][j]+=ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1]; res=max(res,ans[i][j]); } printf("%d\n",res+bs); return 0; }