贡献题多考虑考虑差分吧🐺
题意:给定一个n*n大小的网格,上面有两种颜色黑和白,要求使用k*k的橡皮擦擦一次,输出最多的白线(全为白的行和列)数目。
这里的行与列其实互不影响,我们分别算出来行列然后相加就可以,
但是怎么判断一个左上角在(x,y)位置的橡皮擦能得到多少全白的行/列实在想不到了。。。
一开始想预处理出来每一行白块的个数(该题和个数关系不大),但是脑子稍微转一转就能想到没什么用啊摔!(ノ`Д)ノ
能维护的差不多就那么几样,对于每一行,应该找的是每一行最左边和最右边黑块的位置,这样才能判断出橡皮擦到底能不能覆盖全。
预处理完成之后,怎么计算答案也是个问题,知道这一行左右黑块的位置之后,就能知道完全覆盖该区域的橡皮擦都是那些,
对这些区域的橡皮擦,贡献加一,但是对整块答案直接加一太耗了,所以使用二维差分进行计算,最后加和。
#include<bits/stdc++.h> using namespace std; #define endll '\n' #define C getchar() typedef long long ll; #define pi acos(-1.0) #define INF 0x3f3f3f3f #define mod 998244353 #define pii pair<int, int> const int MAXN = 1e6 + 10; #define stop system("pause") #define lowbit(x) ((x) & (-x)) #define Temp template<typename T> #define FOR(i,a,b) for(int i=a;i<=b;i++) #define mem(a, b) memset(a, b, sizeof(a)) #define fast std::ios::sync_with_stdio(false) Temp inline void rd(T &s) { s = 0;T t = 1, k = C; for (; k < '0' || k > '9'; k = C)if (k == '-') t = -1; for (; k >= '0' && k <= '9'; k = C) s = (s << 1) + (s << 3) + (k ^ 48); s *= t; } int n,k; char mp[2222][2222]; int p[2222][2222]; void add(int x1,int y1,int x2,int y2) { ++p[x1][y1],++p[x2+1][y2+1]; --p[x1][y2+1],--p[x2+1][y1]; } int main() { rd(n),rd(k); for(int i=0;i<n;++i) scanf("%s",mp[i]); for(int i=0;i<n;++i)//每行 { int b=-1,e=-1; for(int j=0;j<n;++j) { if(mp[i][j]=='B') { if(b==-1) b=j; e=j; } } if(b==-1) add(0,0,n-1,n-1);//如果该行没有黑色,那么所有位置的橡皮贡献加一 else if(e-b+1<=k)//橡皮大小足够覆盖 add(max(0,i-k+1),max(0,e-k+1),i,b);//对能覆盖这片区域的橡皮擦贡献加一 } for(int j=0;j<n;++j)//每列 { int b=-1,e=-1; for(int i=0;i<n;++i) { if(mp[i][j]=='B') { if(b==-1) b=i; e=i; } } if(b==-1) add(0,0,n-1,n-1);//如果该列没有黑色,那么所有位置的橡皮贡献加一 else if(e-b+1<=k)//橡皮大小足够覆盖 add(max(0,e-k+1),max(0,j-k+1),b,j);//对能覆盖这条区域的橡皮擦贡献加一 } int ans=0; for(int i=0;i<n;++i) for(int j=1;j<n;++j) p[i][j]+=p[i][j-1]; for(int i=1;i<n;++i) for(int j=0;j<n;++j) p[i][j]+=p[i-1][j]; for(int i=0;i<n;++i) for(int j=0;j<n;++j) ans=max(ans,p[i][j]); cout<<ans<<endll; //stop; return 0; }