[ARC135D] Add to Square

发布时间 2023-12-13 07:53:23作者: Hypercube07

不妨将棋盘黑白染色,并将黑色格子上的数取反。对应地,把操作修改为将某个 \(2 \times 2\) 区域地黑格子 \(-x\),白格子 \(+x\) 后答案与原问题相同。于是我们考虑这个新问题的解(不难发现新问题和原问题的解集是一一对应的)。

对于新问题,修改显然不会影响行或列的和。实际上只要对应行、列相等也是矩阵间能相互变换的充要条件(注意到操作可逆)。必要性显然,充分性则考虑容易通过操作将矩阵变为除最后一行、最后一列全 \(0\),两个矩阵能变为同一种状态,自然也可以相互变换。

接下来我们其实要做的是:给定矩阵每一行、列的和,要求构造一个矩阵使得元素绝对值和最小。

实际上,设矩阵第 \(i\) 行的和为 \(h_i\),第 \(j\) 列的和为 \(l_j\),则 \(ans=min(\sum|h_i|,\sum|l_j|)\)

注意到显然有 \(ans \geq min(\sum|h_i|,\sum|l_j|)\),所以下面我们给出 \(ans\) 达到这个下界的构造:

\(\sum|h_i| \leq \sum|l_j|\),那么一个构造方案合法,当且仅当每一列里的数正负性相同(否则会造成浪费)。不妨记行 \(i\) 还有 \(h'_i\) 要填,列 \(j\) 还有 \(l'_j\) 要填,那么只要一直使 \(h'_i>0\) 的行和 \(l'_j>0\) 的行相消,再使所有 \(h'_i<0\) 的行和 \(l'_j<0\) 的列相消,所有 \(h'_i\) 都会为 \(0\)。接下来只需要将 \(h'_{j_1}>0\) 的列和 \(h'_{j_2}<0\) 的列相互抵消即可。不难证明构造一定合法,且由于每次都会消去一行或一列,构造的复杂度为 \(O(n)\)

总时间复杂度 \(O(n^2)\)

代码细节
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef double db;
inline ll read() {
	ll x=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const ll N=505;
ll n,m,a[N][N],b[N][N],suml[N],sumh[N],ans[N][N];
bool flg;
void chg() {
	flg^=1;
	for(int i=1;i<=n;i++) 
		for(int j=1;j<=m;j++)
		    b[j][i]=a[i][j];
	swap(n,m);
	for(int i=1;i<=max(n,m);i++) sumh[i]=suml[i]=0;
	for(int i=1;i<=n;i++) 
        for(int j=1;j<=m;j++) {
        	a[i][j]=b[i][j];
        	sumh[i]+=a[i][j],suml[j]+=a[i][j];
		}
}
int main() {
    n=read(),m=read();
    for(int i=1;i<=n;i++) 
        for(int j=1;j<=m;j++) {
        	a[i][j]=read()*(((i+j)&1)?-1:1);
        	sumh[i]+=a[i][j],suml[j]+=a[i][j];
		}
	ll SH=0,SL=0;
	for(int i=1;i<=n;i++) SH+=abs(sumh[i]);
	for(int i=1;i<=m;i++) SL+=abs(suml[i]);
	cout<<max(SH,SL)<<'\n';
	if(SL<SH) chg();
	queue <int> h_z,h_f,l_z,l_f;
	for(int i=1;i<=n;i++) {
		if(sumh[i]>0) h_z.push(i);
		if(sumh[i]<0) h_f.push(i);
	}
	for(int i=1;i<=m;i++) {
		if(suml[i]>0) l_z.push(i);
		if(suml[i]<0) l_f.push(i);
	}
	while(!h_z.empty()) {
		int nw_h=h_z.front(),nw_l=l_z.front();h_z.pop();l_z.pop();
		ll val=min(sumh[nw_h],suml[nw_l]);
		ans[nw_h][nw_l]+=val,sumh[nw_h]-=val,suml[nw_l]-=val;
		if(sumh[nw_h]) h_z.push(nw_h);
		if(suml[nw_l]) l_z.push(nw_l);
	}
	while(!h_f.empty()) {
		int nw_h=h_f.front(),nw_l=l_f.front();h_f.pop();l_f.pop();
		ll val=max(sumh[nw_h],suml[nw_l]);
		ans[nw_h][nw_l]+=val,sumh[nw_h]-=val,suml[nw_l]-=val;
		if(sumh[nw_h]) h_f.push(nw_h);
		if(suml[nw_l]) l_f.push(nw_l);
	}
	while(!l_z.empty()&&!l_f.empty()) {
		int nw_z=l_z.front(),nw_f=l_f.front();l_z.pop();l_f.pop();
		ll val=min(abs(suml[nw_z]),abs(suml[nw_f]));
		ans[1][nw_z]+=val,ans[1][nw_f]-=val,suml[nw_z]-=val,suml[nw_f]+=val;
		if(suml[nw_z]) l_z.push(nw_z);
		if(suml[nw_f]) l_f.push(nw_f);
	}
	assert(l_z.empty()&&l_f.empty());
	if(flg) {
		swap(n,m);
		for(int i=1;i<=n;i++)
		    for(int j=1;j<=m;j++) {
		    	cout<<ans[j][i]*(((i+j)&1)?-1:1)<<' ';
		    	if(j==m) cout<<'\n';
			}
	}
	else {
		for(int i=1;i<=n;i++)
		    for(int j=1;j<=m;j++) {
		    	cout<<ans[i][j]*(((i+j)&1)?-1:1)<<' ';
		    	if(j==m) cout<<'\n';
			}
	}
	return 0;
}