CF1200D 【White Lines】

突然发现自己的做法很清奇,于是就来写一篇题解了。

觉得自己还是可以,居然没用二维的东西维护答案

题意是在一个黑白矩阵上选一个小矩形染色为白,让全白的行列最多。

Part.1

考虑一种最暴力的做法,我们枚举小矩阵的左上角,暴力染色后统计行列的数量。

这个算法的时间复杂度为\(O((n-k)^2*k)\),可以通过\(n,k<=400\)\(n,k\)值接近的点。

但这个算法在\(k=\frac{n}{2}\)时复杂度会被卡成\(O(n^3)\)但如果出题人数据水可能可以比正解跑的快

Part.2

先来考虑一列的答案。

如果这一列全是白色,无论在哪里时它都有贡献,先预处理出来。

考虑我们在每次枚举到\(i,j\),往\(i,j+1\)继续枚举时,我们只用转移一列,如图。

此时只有两边的答案更新了,我们只需要考虑如何更新这两列的答案即可。

考虑对每一列做一个前缀和统计黑点的个数,每次从答案中减去左边那列的贡献,加上右边的贡献就可以了(贡献就是如果这段的黑点个数等于这列的黑点个数,答案就加一)。

Part.3

我们再来考虑如何统计一行的贡献。

如果这一行全是白色,同理的,先预处理出来。

同样的,我们在从\(i\)\(i+1\)枚举时,也只用更新一行的答案。

考虑如何统计一行的贡献,先只看一行,我们枚举到\(i\)时包含了\([i,i+k-1]\)这个区间。

我们考虑只有这个区间包含了所有黑点时才对答案有贡献,所以我们只用记录下最左边的黑点与最右边的黑点。

如图,只有在包含了整个区间时才有贡献,如果它的区间大小没有整个区间大,那这行就永远没有贡献。

所以拓展到\(k\)行,我们只需要计算枚举到\(i\)时每行的一个区间时对答案是否有贡献即可。

但这里是不是要用到数据结构呢,实际上不用,我们这里的问题比这个要弱的多。

显然\([i,i+k-1]\)这个区间它只有它的左端点决定,我们可以只统计这个区间的左端点在哪个区间合法。

设左黑点的位置是\(L\),右黑点的位置是\(R\),那么整个区间能被全覆盖的充要条件是\(R-k+1\leq i\leq L\),那么我们只需要对\([R-k+1,L]\)区间打标记即可。

这里这个步骤的的实现不需要用任何数据结构,考虑我们只用枚举的每一行进行一次这样的操作,需要\(n-k\)次,但是我们的查询操作需要每个位置进行一次,即\((n-k)^2\)次。所以我们可以直接用一个数组暴力打标记,这样是\(O(n)\)的,查询查一个点,是\(O(1)\)的,可以通过。

总时间复杂度除开读入外是\(O((n-k)\times n)\)的,可以通过。

\(Code\)

#include <cstdio>
#include <iostream>
using namespace std;
const int maxn=2005;

char s[maxn][maxn];
int sum[maxn][maxn];
int vis[maxn],n,k;
int L[maxn],R[maxn],hans,lans,ans,llans;

inline int max(const int &x,const int &y)
{
    return x>y?x:y;
}

int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;++i)
        scanf("%s",s[i]+1);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            if(s[i][j]=='B') {L[i]=j;break;}
    for(int i=1;i<=n;++i)
        for(int j=n;j>0;--j)
            if(s[i][j]=='B') {R[i]=j;break;}
    for(int j=1;j<=n;++j)
    {
        for(int i=1;i<=n;++i)
        {
            sum[i][j]=sum[i-1][j];
            if(s[i][j]=='B') sum[i][j]++;
        }
        if(sum[n][j]==0) llans++;
    }
    for(int i=1;i<=n;++i)
        if(L[i]==0&&R[i]==0) hans++;
    for(int i=1;i<=k;++i)
    {
        if(R[i]-L[i]+1>k) continue;
        for(int j=max(1,R[i]-k+1);j<=L[i];++j)
            ++vis[j];
    }
    for(int i=1;i<=n-k+1;++i)
    {
        if(i!=1)
        {
            for(int j=max(1,R[i-1]-k+1);j<=L[i-1];++j) 
                --vis[j];
            for(int j=max(1,R[i+k-1]-k+1);j<=L[i+k-1];++j) 
                ++vis[j];
        }
        lans=llans;
        for(int j=1;j<=k;++j)
            if(sum[n][j]&&sum[i+k-1][j]-sum[i-1][j]==sum[n][j]) lans++;
        ans=max(ans,lans+vis[1]);
        for(int j=2;j<=n-k+1;++j)
        {
            if(sum[n][j-1]&&sum[i+k-1][j-1]-sum[i-1][j-1]==sum[n][j-1]) lans--;
            if(sum[n][j+k-1]&&sum[i+k-1][j+k-1]-sum[i-1][j+k-1]==sum[n][j+k-1]) lans++;
            ans=max(ans,lans+vis[j]);
        }
    }
    printf("%d\n",ans+hans);
    return 0;
}

图是不是有点丑

posted @ 2020-07-27 19:55  试试事实上吗  阅读(223)  评论(3编辑  收藏  举报
Live2D