【2020五校联考NOIP #8】自闭

题目传送门
题意:
有一个 \(n \times m\) 的矩阵,里面已经填好了 \(k\) 个非负整数。
问是否能在其它 \(n \times m-k\) 个格子里各填上一个非负整数,使得得到的矩阵满足以下条件:

  • 在任意一个 \(2 \times 2\) 的子矩阵中,左上角的数 \(+\) 右下角的数 \(=\) 右上角的数 \(+\) 左下角的数。

如果可以输出 Zibi 否则输出 Zikai
\(n,m,k \in [1,10^5]\)

考虑探究这个矩阵有什么性质。假设有一个 \(3 \times 3\) 的子矩阵:

\[\begin{bmatrix}a&d&g\\b&e&h\\c&f&i\end{bmatrix} \]

根据上面的条件我们有:

\[\begin{cases}a+e=b+d\\b+f=c+e\\d+h=e+g\\e+i=f+h\end{cases} \]

由第一个和第三个式子可以得到 \(a+f=g+c\)
由第二个和第四个式子可以得到 \(d+i=g+f\)
再把这两个式子相结合一下可以得到 \(a+i=c+d\)
于是我们初步得到一个结论:如果有个满足条件的矩阵 \(A\),那么对于任意 \(x_1,x_2 \in [1,n],y_1,y_2 \in [1,m],x_1 \neq x_2,y_1 \neq y_2\),都有

\[A_{x_1,y_1}+A_{x_2,y_2}=A_{x_1,y_2}+A_{x_2,y_1} \]

根据这个性质,考虑固定住第一行和第一列,根据上面的性质填好剩下 \((n-1)\times(m-1)\) 个数。
那么显然有 \(A_{i,j}=A_{1,j}+A_{i,1}-A_{1,1}\)
我们令 \(r_1=0,r_i(i>2)=A_{i,1}-A_{1,1}\)
再令 \(c_i=A_{1,i}\)
那么对于任意 \(i \in [1,n],j \in [1,m]\),你把 \(r_i+c_j\) 计算出来就会得到 \(A_{i,j}=r_i+c_j\)
现在我们的目标是找出是否存在满足条件的 \(r_i,c_i\)

我们假设同一行中有两个已经填好的数 \(A_{i,y_1}=v_1,A_{i,y_2}=v_2\)
那么 \(v_1=r_i+c_{y_1},v_2=r_i+c_{y_2}\)
两式相减可以得到 \(c_{y_1}-c_{y_2}=v_1-v_2\)
在顶点 \(y_1,y_2\) 之间连一条有向边边,边权为 \(v_1-v_2\)
这样建边看似是 \(n^2\) 的,不过注意到如果已知 \(c_{y_1}-c_{y_2}=v_1-v_2,c_{y_1}-c_{y_3}=v_1-v_3\),就可以直接得出 \(c_{y_2}-c_{y_3}=v_2-v_3\),所以可以把第 \(i\) 行填好数的位置按列数从小到大排序,只用在相邻顶点之间建边,这样边数最多是 \(\mathcal O(n)\) 的。
然后你对于每个连通块进行一遍 \(dfs\),求出该连通块中编号最小的点 \(x\) 到每个点 \(i\) 的距离 \(dist_i\),那么真正的 \(c_i=c_x+dist_i\)
然后你对于每条边 \((u,v,w)\) 都检查 \(dist_v\) 是否与 \(dist_u+w\) 相等就行了。
由于我们每次连边都是从编号小的点连向编号大的点的,所以把每个连通块中编号最小的点作为基准是没问题的。

等等我们好像忽略了一个条件。
题目中要求填上一个非负整数,而我们只检验了是否可以填整数,而忽视了非负这个条件。
怎样解决呢?
对于每一行,假设有一个位置 \(j\) 填上了数 \(v\)\(j\) 所在列的连通块中编号最小的点为 \(x\)
那么 \(r_i+c_j=v\),即 \(r_i+c_x+dist_j=v\)
于是我们得到 \(r_i+c_x=v-dist_j\)
再假设该连通块中 \(dist_k\) 最小的点 \(z\)
那么 \(A_{i,z}=r_i+c_z=r_i+c_x+dist_z\)
如果 \(r_i+c_x+dist_z<0\) 就说明 \(A_{i,z}<0\),不合法。
于是这题就做完了。

/*
Contest: -
Problem: NFLSOJ 713
Author: tzc_wk
Time: 2020.10.20
*/
#include <bits/stdc++.h>
using namespace std;
#define fi			first
#define se			second
#define pb			push_back
#define fz(i,a,b)	for(int i=a;i<=b;i++)
#define fd(i,a,b)	for(int i=a;i>=b;i--)
#define foreach(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define all(a)		a.begin(),a.end()
#define fill0(a)	memset(a,0,sizeof(a))
#define fill1(a)	memset(a,-1,sizeof(a))
#define fillbig(a)	memset(a,0x3f,sizeof(a))
#define y1			y1010101010101
#define y0			y0101010101010
typedef pair<int,int> pii;
typedef long long ll;
int n,m,k;
struct data{
	int x,y,z;
} a[200005];
bool cmpx(data x,data y){
	return x.x<y.x;
}
bool cmpy(data x,data y){
	return x.y<y.y;
}
struct edge{
	int u,v,w;
} edr[200005],edc[200005];
int ecntr=0,ecntc=0;
vector<pii> gr[200005],gc[200005];
vector<data> dr[200005],dc[200005];
int disr[200005],disc[200005];
inline void adder(int u,int v,int w){
	edr[++ecntr]={u,v,w};
	gr[u].pb(make_pair(v,w));
}
inline void addec(int u,int v,int w){
	edc[++ecntc]={u,v,w};
	gc[u].pb(make_pair(v,w));
}
bool visr[200005],visc[200005];
int compr=0,compc=0;
int fromr[200005],fromc[200005];
inline void dfsr(int x){
	if(visr[x]) return;
	visr[x]=1;fromr[x]=compr;
	for(int i=0;i<gr[x].size();i++){
		int y=gr[x][i].fi,z=gr[x][i].se;
		disr[y]=disr[x]+z;dfsr(y);
	}
}
inline void dfsc(int x){
	if(visc[x]) return;
	visc[x]=1;fromc[x]=compc;
	for(int i=0;i<gc[x].size();i++){
		int y=gc[x][i].fi,z=gc[x][i].se;
		disc[y]=disc[x]+z;dfsc(y);
	}
}
int mnr[200005],mnc[200005];
int main(){
//	freopen("C:\\Users\\汤\\Downloads\\problem_714\\subtask2\\zibi3.in","r",stdin);
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=k;i++){
		data in;scanf("%d%d%d",&in.x,&in.y,&in.z);
		dr[in.x].push_back(in);dc[in.y].push_back(in);a[i]=in;
	}
	for(int i=1;i<=n;i++) sort(all(dr[i]),cmpy);
	for(int i=1;i<=m;i++) sort(all(dc[i]),cmpx);
	for(int i=1;i<=n;i++){
		if(dr[i].empty()) continue;
		for(int j=1;j<dr[i].size();j++){
			addec(dr[i][j-1].y,dr[i][j].y,dr[i][j].z-dr[i][j-1].z);
			addec(dr[i][j].y,dr[i][j-1].y,dr[i][j-1].z-dr[i][j].z);
		}
	}
	for(int i=1;i<=m;i++){
		if((dc[i].empty())) continue;
		for(int j=1;j<dc[i].size();j++){
			adder(dc[i][j-1].x,dc[i][j].x,dc[i][j].z-dc[i][j-1].z);
			adder(dc[i][j].x,dc[i][j-1].x,dc[i][j-1].z-dc[i][j].z);
		}
	}
	for(int i=1;i<=n;i++) if(!visr[i]) compr++,dfsr(i);
	for(int i=1;i<=m;i++) if(!visc[i]) compc++,dfsc(i);
	for(int i=1;i<=ecntr;i++){
		if(disr[edr[i].v]!=disr[edr[i].u]+edr[i].w)
			return puts("Zikai"),0;
	}
	for(int i=1;i<=ecntc;i++){
		if(disc[edc[i].v]!=disc[edc[i].u]+edc[i].w)
			return puts("Zikai"),0;
	}
	for(int i=1;i<=compr;i++) mnr[i]=0x3f3f3f3f;
	for(int i=1;i<=compc;i++) mnc[i]=0x3f3f3f3f;
//	puts("A");
	for(int i=1;i<=n;i++) mnr[fromr[i]]=min(mnr[fromr[i]],disr[i]);
	for(int i=1;i<=m;i++) mnc[fromc[i]]=min(mnc[fromc[i]],disc[i]);
	for(int i=1;i<=n;i++){
		int mn=0x3f3f3f3f;
		for(int j=0;j<dr[i].size();j++){
			int x=dr[i][j].y;
			mn=min(mn,dr[i][j].z-disc[x]+mnc[fromc[x]]);
//			printf("%d %d %d\n",i,x,dr[i][j].z-disc[x]+mnc[fromc[x]]);
		}
//		printf("%d %d\n",i,mn);
		if(mn<0) return puts("Zikai"),0;
	}
	puts("Zibi");
	return 0;
}
posted @ 2020-10-22 10:30  tzc_wk  阅读(203)  评论(2)    收藏  举报