Codeforces Round #578 (Div. 2)
D题
题目大意:有一个N*N(1<=N<=2000)方阵,每个点非黑即白,现在可以选一个 K * K(1<=K<=N) 的子矩阵,将其中黑点全部变白,统计操作后方阵完全为白色的行的行数,完全为白色的列的列数,求二者之和最大值。
N^2*K的暴力很好想,预处理每行每列的黑点数,以及每个点左边的黑点数,上边的黑点数,N^2跑每个点,两趟K次得到答案,然而复杂度过不了。
问题还是出在重复计算上,如果将子矩阵看作窗口,那么该窗口的列答案在左右移动时可以被继承很多(不用重复算),行答案在上下移动时也可被继承。可将行贡献与列贡献二者分开处理,用移动窗口的思路写。
1 #pragma optimize("O3","unroll-loops") 2 #pragma target("avx3") 3 4 #include <bits/stdc++.h> 5 using namespace std; 6 7 const int maxn = 2012; 8 typedef pair<int , int > pii; 9 pii LR[maxn] , UD[maxn]; 10 char raw[maxn][maxn]; 11 pii dp[maxn][maxn]; 12 13 inline bool COVER(pii x, pii y){// x cover y 14 return x.first <= y.first && y.second <= x.second; 15 } 16 17 18 int main(){ 19 int ever = 0; 20 int N,K; 21 scanf("%d%d",&N,&K); 22 for (int i = 1; i <= N; ++i) { 23 scanf("%s",raw[i] + 1); 24 LR[i] = make_pair(-1,-1); 25 bool evermeet = false; 26 for(int j =1;j<=N;++j){ 27 if(raw[i][j] == 'B') { 28 LR[i].second = j; 29 if(!evermeet) evermeet = true, LR[i].first = j; 30 } 31 } 32 if(!evermeet) ever ++; 33 } 34 for(int j=1;j<=N;++j){ 35 UD[j] = make_pair(-1,-1); 36 bool evermeet = false; 37 for(int i = 1;i<=N;++i){ 38 if(raw[i][j] == 'B'){ 39 UD[j].second = i; 40 if(!evermeet) evermeet = true , UD[j].first = i; 41 } 42 } 43 if(!evermeet) ever ++; 44 } 45 for(int i = 1;i<= N - K +1;++i){ 46 pii curUD = make_pair(i,i+K-1); 47 for(int j = 1;j<=K;++j) 48 if(COVER(curUD,UD[j])) 49 dp[i][1].first ++; 50 for(int j = 2;j<=N - K + 1;++j){ 51 dp[i][j].first = dp[i][j-1].first; 52 if(COVER(curUD,UD[j-1])) dp[i][j].first --; 53 if(COVER(curUD,UD[j+K-1])) dp[i][j].first ++; 54 } 55 } 56 for (int j = 1; j <= N - K + 1; j++) { 57 pii curLR = make_pair(j,j+K-1); 58 for(int i = 1;i<=K;++i) 59 if(COVER(curLR,LR[i])) 60 dp[1][j].second ++; 61 for(int i = 2;i<=N-K+1;++i){ 62 dp[i][j].second = dp[i-1][j].second; 63 if(COVER(curLR,LR[i-1])) dp[i][j].second --; 64 if(COVER(curLR,LR[i+K-1])) dp[i][j].second ++; 65 } 66 } 67 int ans = ever; 68 for(int i=1;i<=N;++i) 69 for(int j=1;j<=N;++j) 70 ans = max(ans,dp[i][j].first + dp[i][j].second + ever); 71 printf("%d\n",ans); 72 return 0; 73 }
这题有很多种处理行贡献与列贡献的方法,预处理最左最右和最高最低是一种,预处理每个点左边与上边的黑点数也行。(完全的白列白行可特殊处理记录,好写一点)关键在于滑动窗口继承之前部分答案的思想。
posted on 2019-09-16 23:54 Emiya_Kiritsugu 阅读(170) 评论(0) 编辑 收藏 举报