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\) 是合法的,此时显然可以得到:
此时互相代入展开 \(c_1,r_1\):
我们就得到了 \(r_i,c_i\) 的表达式。
显然 \(D\) 合法的充要条件是 \(D_{i,j}=r_i+c_j\),我们代入 \((1)\) 得到:
同时我们也知道 \(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\) 表示(其他都是常数)的式子:
于是我们会发现,这个式子可以转化为一个 \(\sum |r_1-\lambda|\) 的形式:
那么此时,根据数学知识,\(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;
}