【洛谷P7515】矩阵游戏
题目
题目链接:https://www.luogu.com.cn/problem/P7515
Alice 有一个 \(n \times m\) 的矩阵 \(a_{i, j}\)(\(1 \le i \le n\),\(1 \le j \le m\)),其每个元素为大小不超过 \({10}^6\) 的非负整数。
Bob 根据该矩阵生成了一个 \((n - 1) \times (m - 1)\) 的矩阵 \(b_{i, j}\)(\(1 \le i \le n - 1\),\(1 \le j \le m - 1\)),每个元素的生成公式为
现在 Alice 忘记了矩阵 \(a_{i, j}\),请你根据 Bob 给出的矩阵 \(b_{i, j}\) 还原出 \(a_{i, j}\)。
\(n,m\leq 300,Q\leq 10\)。
思路
首先我们直接随意构造出一组解,然后考虑去修改它使所有数都在 \([0,10^6]\) 内。
我们发现任意一行或一列同时修改,其中第 \(i\) 个数修改 \((-1)^i\times k\),可以似的每一个 \(2\times 2\) 的小正方形的和不变。那我们只需要确定每一行的权值 \(c_i\),每一列的权值 \(d_i\),然后就可以把矩阵修改为
也就是对于第 \(i\) 行第 \(j\) 列,我们需要满足
这个东西很像差分约束,但是我们发现矩阵中存在 \(+c_i+d_j\),或 \(-c_i-d_j\) 的部分,我们无法转化为查分约束,所以我们考虑在原矩阵中动手脚。
如果我们把偶数行的 \(c\) 全部取反,奇数行的 \(d\) 全部取反,我们就可以得到
就可以搞差分约束了。
时间复杂度 \(O(Qnm(n+m))\)。注意不加 SLF 优化会 T 飞。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=310;
int Q,n,m,tot,cnt[N*2],head[N*2];
ll a[N][N],dis[N*2];
bool vis[N*2];
int read()
{
int d=0; char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
return d;
}
struct edge
{
int next,to,dis;
}e[N*N*2];
void add(int from,int to,int dis)
{
e[++tot]=(edge){head[from],to,dis};
head[from]=tot;
}
void prework()
{
memset(head,-1,sizeof(head));
memset(a,0,sizeof(a));
tot=0;
}
bool spfa()
{
memset(dis,0x3f3f3f3f,sizeof(dis));
memset(cnt,0,sizeof(cnt));
memset(vis,0,sizeof(vis));
deque<int> q;
q.push_back(1); dis[1]=cnt[1]=0;
while (q.size())
{
int u=q.front(); q.pop_front();
vis[u]=0;
if (cnt[u]>n+m) return 0;
for (int i=head[u];~i;i=e[i].next)
{
int v=e[i].to;
if (dis[v]>dis[u]+e[i].dis)
{
dis[v]=dis[u]+e[i].dis;
cnt[v]=cnt[u]+1;
if (!vis[v])
{
vis[v]=1;
if (dis[v]<dis[q.front()]) q.push_front(v);
else q.push_back(v);
}
}
}
}
return 1;
}
int main()
{
Q=read();
while (Q--)
{
prework();
n=read(); m=read();
for (int i=1;i<n;i++)
for (int j=1;j<m;j++)
a[i+1][j+1]=read()-a[i][j]-a[i+1][j]-a[i][j+1];
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if ((i+j)&1)
add(j+n,i,a[i][j]),add(i,j+n,1e6-a[i][j]);
else
add(i,j+n,a[i][j]),add(j+n,i,1e6-a[i][j]);
if (!spfa()) { printf("NO\n"); continue; }
printf("YES\n");
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
if ((i+j)&1) printf("%d ",(int)(a[i][j]+dis[j+n]-dis[i]));
else printf("%d ",(int)(a[i][j]+dis[i]-dis[j+n]));
printf("\n");
}
}
return 0;
}