[MdOI2020] Decrease

\[ \texttt{Preface} \]

感觉 C 比 B 还简单?
\[ \texttt{Description} \]
给定一个 \(n×n\) 的矩阵,你可以进行若干次操作。

每次操作,你可以将一个 \(k×k\)连续 子矩阵里的所有数全都加上 \(1\) 或者全部都减去 \(1\)

初始时,矩阵中有 \(m\) 个位置上的数不为 \(0\) ,其他位置上的数均为 \(0\)

请你求出至少需要多少次操作,可以将矩形中所有数都变为 \(0\)
\[ \texttt{Solution} \]
为了方便叙述,我们称原矩阵为 \(A\)

\(~\)

Subtask1 : \(n \leq 10^3\)\(k \leq 1\)

很显然,答案为 \(\sum\limits_{i=1}\limits^{n}\sum\limits_{j=1}\limits^{n}|A[i][j]|\)

至此能拿到 \(11pts\)

\(~\)

Subtask2 : \(n \leq 20\)\(k \leq 20\)

我不知道怎么艹过这个子任务欸 qwq 。

理论上是留给暴力选手的。

至此能拿到 \(25pts\)

\(~\)

Subtask3 : \(n \leq 100\)\(k \leq 100\)

有点正解的味道了。

首先我们注意到操作的顺序不会影响答案,且一个 \(k×k\) 的子矩阵不能一会加一会减。

\(~\)

考虑 A[1][1] ,它显然只会被一个 \(k×k\) 的子矩阵覆盖(一个左上 \((1,1)\) ,右下 \((k,k)\) 的子矩阵),那么我们只能通过该矩阵来控制 A[1][1]

那我们直接使得该矩阵中的所有元素减去 A[1][1] 即可,我们就要保证该矩阵不能再被操作了。

接着考虑 A[1][2] ,它只会被两个 \(k×k\) 的子矩阵覆盖(一个左上 \((1,1)\) ,右下 \((k,k)\) 的子矩阵,一个左上 \((1,2)\) ,右下 \((k,k+1)\) 的子矩阵),但是我们要保证左上 \((1,1)\) ,右下 \((k,k)\) 的子矩阵不能被操作,否则就破坏了 A[1][1],所以我们只能通过左上 \((1,2)\) ,右下 \((k,k+1)\) 的子矩阵来控制 A[1][2]

直接使得该矩阵所有元素减去 A[1][2] 即可。

以此类推,对于 \((i,j)\) 满足 \(i,j \leq n-k+1\) 的位置,都能按顺序控制 左上 \((i,j)\) ,右下 \((i+k-1,j+k-1)\) 的子矩阵使得 A[i][j] = 0 ,即令该矩阵所有元素减去 A[i][j] 即可。

那么对于剩下的 \((i,j)\) 不满足 \(i,j \leq n-k+1\) 的位置,则已经不能改变 A[i][j] 的值了,所以说,若在这些位置中找到一个 A[i][j] ≠ 0 ,则说明无解。

暴力去修改矩阵,时间复杂度 \(O(k^2(n-k)^2)\)由小学数学可得,当 \(k=\frac{n}{2}\) 时,复杂度最高,为 \(O(\frac{n^4}{16})\) ,计算量约为 \(6250000\) ,稳的一批。

至此能拿到 \(42pts\)

\(~\)

Subtask4 : \(n \leq 10^3\)\(k \leq 10^3\)

我们发现对矩阵做减法操作可以用一维差分优化到 \(O(k)\)

具体的,开个一位差分数组 S[i][j] ,每次令 左上 \((i,j)\) ,右下 \((i+k-1,j+k-1)\) 的矩阵减去 \(d\) 时,我们只需要令 S[i~i+k-1][j] += d, S[i~i+k-1][j+k] -= d 即可,记得在枚举的过程中要用一维前缀和还原当前状态的 \(S\) 数组。

时间复杂度 \(O(k(n-k)^2)\)由小学数学可得,当 \(k=\frac{n}{3}\) 时,复杂度最高,为 \(O(\frac{4n^3}{27})\) ,计算量约为 \(148148148\) ,没有亲测过,开个 \(\text{O2}\) 应该稳。

至此能拿到 \(76pts\)

\(~\)

Subtask5 : \(n \leq 5×10^3\)\(k \leq 10^3\)

能一维差分干嘛不能二维差分阿

我们发现对矩阵做减法操作可以用二维差分优化到 \(O(1)\)

具体的,开个一位差分数组 S[i][j] ,每次令 左上 \((i,j)\) ,右下 \((i+k-1,j+k-1)\) 的矩阵减去 \(d\) 时,我们只需要令 S[i][j] += d, S[i+k][j] -= d, S[i][j+k] -= d, S[i+k][j+k] += d 即可,记得在枚举的过程中要用二维前缀和还原当前状态的 \(S\) 数组。

时间复杂度 \(O((n-k)^2)\) ,稳的一批。

至此能拿到 \(100pts\)
\[ \texttt{Code} \]

#include<cstdio>
#include<algorithm>

#define RI register int // 卡常1

using namespace std;

namespace IO // 卡常2
{
    static char buf[1<<20],*fs,*ft;
    inline char gc()
    {
        if(fs==ft)
        {
            ft=(fs=buf)+fread(buf,1,1<<20,stdin);
            if(fs==ft)return EOF;
        }
        return *fs++;
    }
    #define gc() getchar()
    inline int read()
    {
        int x=0,f=1;char s=gc();
        while(s<'0'||s>'9'){if(s=='-')f=-f;s=gc();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=gc();}
        return x*f;
    }
}using IO::read;

const int N=5010;

int n,m,k;

long long a[N][N];
long long S[N][N];

long long ans;

int main()
{
    n=read(),m=read(),k=read();

    for(RI i=1;i<=m;i++)
    {
        int x=read(),y=read(),d=read();
        a[x][y]=d;
    }

    for(RI i=1;i<=n;i++)
        for(RI j=1;j<=n;j++)
        {
            S[i][j]=S[i][j]+S[i-1][j]+S[i][j-1]-S[i-1][j-1];
            a[i][j]+=S[i][j];

            if(i+k-1>n||j+k-1>n)
            {
                if(a[i][j]==0)
                    continue;
                else
                {
                    puts("-1");
                    return 0;
                }
            }

            ans+=abs(a[i][j]);
            S[i][j]+=-a[i][j];
            S[i+k][j]-=-a[i][j];
            S[i][j+k]-=-a[i][j];
            S[i+k][j+k]+=-a[i][j];
        }

    printf("%lld\n",ans);

    return 0;
} 

\[ \texttt{Thanks} \ \texttt{for} \ \texttt{watching} \]

猜你喜欢

转载自www.cnblogs.com/cjtcalc/p/12289014.html