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 }
View Code

这题有很多种处理行贡献与列贡献的方法,预处理最左最右和最高最低是一种,预处理每个点左边与上边的黑点数也行。(完全的白列白行可特殊处理记录,好写一点)关键在于滑动窗口继承之前部分答案的思想。

posted on 2019-09-16 23:54  Emiya_Kiritsugu  阅读(170)  评论(0编辑  收藏  举报

导航