loj3500.「联合省选 2021 A」矩阵游戏
值域的限制看起来非常强,很不好弄。
想到 ckw 的一句话:碰到一个题,先想一想如果没有某些操作该怎么做。比如,一个数据结构题如果没有询问怎么做?
于是扔了值域的限制,遂发现变成了个傻逼题,钦定第一行第一列为 \(0\) 然后大力推一遍就好了。
现在再回来考虑值域。有一个显然的调整是每行或每列的每个数依次 \(+c/-c\) (\(c\) 是常数)。假设作用在第 \(i\) 行的 \(c\) 大小为 \(c_i\),列的为 \(d_i\),那么 \(\forall 1\leq i\leq n,1\leq j\leq m\) 有 \(0\leq a_{i,j}\pm c_i\pm d_j\leq10^6\),移项得到 \(-a_{i,j}\leq\pm c_i\pm d_j\leq10^6-a_{i,j}\)。
这玩意又有加又有减的还是没法处理,但我们发现符号的正负是有规律的,\(c,d\) 都是奇正偶负。那么我们把偶数行的 \(c\) 取反,奇数列的 \(d\) 取反,所有的限制都变成了 \(-a_{i,j}\leq\pm(c_i-d_j)\leq10^6-a_{i,j}\),这个就是经典的差分约束了。
写出来之后发现只有 50,原来是恶毒出题人卡常数,加了个 SLF-swap 就过了。
#include<iostream>
#include<cstdio>
#include<deque>
#include<cstring>
using namespace std;
struct edge
{
int nxt,to,weight;
}e[301*301*8];
int t,n,m,s,tot,h[301<<1];
long long a[301][301],b[301][301],dis[301<<1];
int cnt[301<<1];
bool vis[301<<1];
inline int read()
{
int x=0;
char c=getchar();
while(c<'0'||c>'9')
c=getchar();
while(c>='0'&&c<='9')
{
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x;
}
void print(long long x)
{
if(x>=10)
print(x/10);
putchar(x%10+'0');
}
inline void add(int x,int y,int w)
{
e[++tot].nxt=h[x];
h[x]=tot;
e[tot].to=y;
e[tot].weight=w;
}
inline bool SPFA()
{
deque<int> q;
for(register int i=1;i<=s;++i)
{
dis[i]=1ll<<60;
cnt[i]=vis[i]=0;
}
q.push_back(s);
dis[s]=0;
while(!q.empty())
{
int k=q.front();
q.pop_front();
vis[k]=0;
if(++cnt[k]>=n+m)
return 0;
for(register int i=h[k];i;i=e[i].nxt)
if(dis[e[i].to]>dis[k]+e[i].weight)
{
dis[e[i].to]=dis[k]+e[i].weight;
if(!vis[e[i].to])
{
vis[e[i].to]=1;
if(dis[q.front()]>dis[e[i].to])
{
q.push_back(q.front());
q.pop_front();
q.push_front(e[i].to);
}
else
q.push_back(e[i].to);
}
}
}
return 1;
}
signed main()
{
t=read();
while(t--)
{
n=read(),m=read();
tot=0;
memset(e,0,sizeof e);
memset(h,0,sizeof h);
for(register int i=1;i<n;++i)
for(register int j=1;j<m;++j)
b[i][j]=read();
for(register int i=2;i<=n;++i)
for(register int j=2;j<=m;++j)
{
a[i][j]=b[i-1][j-1];
b[i][j-1]-=b[i-1][j-1];
b[i-1][j]-=b[i-1][j-1];
b[i][j]-=b[i-1][j-1];
}
s=n+m+1;
for(register int i=1;i<=n;++i)
add(s,i,0);
for(register int i=1;i<=m;++i)
add(s,i+n,0);
for(register int i=1;i<=n;++i)
for(register int j=1;j<=m;++j)
{
int minn=a[i][j],maxn=1e6-a[i][j];
if((i+j)&1)
{
add(i,j+n,maxn);
add(j+n,i,minn);
}
else
{
add(j+n,i,maxn);
add(i,j+n,minn);
}
}
if(!SPFA())
{
puts("NO");
continue;
}
puts("YES");
for(register int i=1;i<=n;++i,putchar('\n'))
for(register int j=1;j<=m;++j,putchar(' '))
if((i+j)&1)
print(a[i][j]+dis[j+n]-dis[i]);
else
print(a[i][j]+dis[i]-dis[j+n]);
}
return 0;
}