CSP模拟 矩阵操作

涉及知识点:就是个推式子+贪心?

前言

感觉有点板,故记录一下以备后续所用。

题意

有两个 $ n\times m$ 的矩阵 \(A\)\(B\),每次操作可以把 \(A\) 或者 \(B\) 的某一行/列全部 \(+1\),最少几次操作 \(A=B\)

思路

首先想到的肯定是构造一个差分矩阵,即 \(D=B-A\),问题转化为从一个零矩阵开始每次某一行/列 \(+1\)\(-1\),问多少次操作得到 \(D\)。这是因为在 \(A\)\(+1\) 的操作可以等价为在 \(B\)\(-1\) 的操作。注意:\(D\) 中可能有负数。

那么现在问题的规模就从涉及两个矩阵压缩到了一个矩阵上,我们定义 \(r_i\)\(D\)\(i\) 行的修改,\(c_i\) 为第 \(i\) 列的修改,同样的,\(r_i,c_i\) 也可能为负数。

我们先假设 \(D\) 是合法的,此时显然可以得到:

\[\begin{cases} r_i= D_{i,1}-c_1\\ c_i= D_{1,i}-r_1 \end{cases}\tag{1} \]

此时互相代入展开 \(c_1,r_1\)

\[\begin{cases} r_i= D_{i,1}-D_{1,1}+r_1\\ c_i= D_{1,i}-D_{1,1}+c_1 \end{cases}\tag{2} \]

我们就得到了 \(r_i,c_i\) 的表达式。

显然 \(D\) 合法的充要条件是 \(D_{i,j}=r_i+c_j\),我们代入 \((1)\) 得到:

\[D_{i,j}=D_{i,1}+D_{1,i}-(r_1+c_1)\tag{3} \]

同时我们也知道 \(r_1+c_1=D_{1,1}\),因此 \(D\) 合法的条件就是,对于每个 \(i\in[1,n],j\in[1,m]\) 都满足 \(D_{i,j}=D_{i,1}+D_{1,i}-D_{1,1}\)

接下来我们来计算答案,显然答案为 \(\sum_{i=1}^n|r_i|+\sum_{i=1}^m|c_i|\),将 \(c_i\)\((1)\) 中的表示代入,\(r_i\)\((2)\) 中的表示代入,我们就得到了一个全部由 \(r_1\) 表示(其他都是常数)的式子:

\[Ans=\sum_{i=1}^n|D_{i,1}-D_{1,1}+r_1|+\sum_{i=1}^m|D_{1,i}-r_1|\tag{4} \]

于是我们会发现,这个式子可以转化为一个 \(\sum |r_1-\lambda|\) 的形式:

\[Ans=\sum_{i=1}^n|r_1-(D_{1,1}-D_{i,1})|+\sum_{i=1}^m|r_1-D_{1,i}|\tag{5} \]

那么此时,根据数学知识,\(r_1\) 取所有\(n+m\)\(\lambda\) 的中位数即可。

代码

为了节约空间,没必要再新开一个 \(D\) 数组,故直接用 \(B\) 存储差分后的结果。

另外,在编码时,如果 \(n+m\) 为偶数,\(r_1\) 没必要严格取计算中间两个数的平均值,可以证明只要 \(r_1\) 的取值在这两个数之间(包含这两个数)答案都一样,因此随便取左边或者右边那个数就行。

#include<bits/stdc++.h>
#define getid(x,y) ((x-1)*m+y)
using namespace std;
template<class T>inline void rd(T &x){
    T res=0,f=1;
    char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1; ch=getchar();}
    while('0'<=ch && ch<='9'){res=res*10+ch-'0';ch=getchar();}
    x=res*f;
}
template<class T>inline void wt(T x,char endch='\0'){
    static char wtbuff[20];
    static int wtptr;
    if(x==0){
        putchar('0');
    }
    else{
        if(x<0){x=-x;putchar('-');}
        wtptr=0;
        while(x){wtbuff[wtptr++]=x%10+'0';x/=10;}
        while(wtptr--) putchar(wtbuff[wtptr]);
    }
    if(endch!='\0') putchar(endch);
}
typedef long long LL;
const int MAXN=1e5+5;
int n,m;
LL a[MAXN],b[MAXN],ans;
inline void solve(){
    rd(n);rd(m);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            rd(a[getid(i,j)]);
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            rd(b[getid(i,j)]);
            b[getid(i,j)]-=a[getid(i,j)];
        }
    }
    // for(int i=1;i<=n;i++){
    //     for(int j=1;j<=m;j++){
    //         cout<<b[getid(i,j)]<<' ';
    //     }
    //     cout<<endl;
    // }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(b[getid(i,j)]!=b[getid(1,j)]+b[getid(i,1)]-b[getid(1,1)]){
                puts("-1");
                return;
            }
        }
    }
    vector<LL>vec;
    for(int i=1;i<=n;i++)
        vec.push_back(b[getid(1,1)]-b[getid(i,1)]);
    for(int i=1;i<=m;i++)
        vec.push_back(b[getid(1,i)]);
    nth_element(vec.begin(),vec.begin()+vec.size()/2,vec.end());
    LL x=vec[vec.size()/2];
    ans=0;
    for(auto it:vec){
        ans+=abs(it-x);
    }
    wt(ans,'\n');
}
int main(){
    int t;
    rd(t);
    while(t--){
        solve();
    }
    return 0;
}
posted @ 2024-09-10 15:59  MessageBoxA  阅读(19)  评论(0编辑  收藏  举报