UOJ#41. 【清华集训2014】矩阵变换 构造

原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ41.html

题解

首先写个乱搞:

初始每行选择第一个非零元素。然后不断进行如下调整,直到没有两行选择了相同的数:

任取选了相同数的两行,更新被选数较为靠前的那行,即取该行后一个非负整数。

交上去。

过了。

wtf?

然后发现证明这个结论我花的时间远远大于AC这题QAQ

现在我们来证明一下:

引理1

  如果这样调整最终能够顺利结束,那么必然得到一组合法解。

证明:

  首先,任意两行互不相同,所以如果解不合法,假设第 $i$ 行选择了第 $p_i$ 个位置,那么只能存在某两行 $i,j$,满足 $p_i < p_j$,且第 $j$ 行的处于 $(p_i,p_j)$ 中的某个位置上的数 $x$ 与第 $i$ 行选择的数相同。
  但是,由于第 $j$ 行选择的 $x$ 已经被调整掉了,所以一定存在另一行,选择了一个更靠后的 $x$,这与任意两行互不相同矛盾。
  所以引理得证。

 

然后,我们来分两步证明一定有解。

引理2

  假设当前状态下,我们在所有行选择的元素构成的集合为 S ;设若干次更新更新后的集合为 S' ,那么一定有: $S\subseteq S'$ 。

  证明:只有在某两行选择相同的数时会更新,那么这两行至少保留一行,所以原本处于集合中的元素不会消失。

 

接下来我们证明一个命题。

引理3

  以这样的方式,一定可以找到一组解。

  证明:

    首先,只有当在更新某一行时没有下一个非负整数时才找不到解。

    根据引理2,我们知道如果找不到解,则必然有某个数从未出现在被选集合中。
    但是每个数在每一行出现一次,
    而我们不可能在不把这个元素更新掉的情况下把整一行更新空。

    于是又得到矛盾,证明了结论。

 

结合引理1和引理3,我们一定可以找到满足合法解!

代码

#pragma GCC optimize("Ofast","inline")
#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof (x))
#define For(i,a,b) for (int i=a;i<=b;i++)
#define Fod(i,b,a) for (int i=b;i>=a;i--)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define _SEED_ ('C'+'L'+'Y'+'A'+'K'+'I'+'O'+'I')
#define outval(x) printf(#x" = %d\n",x)
#define outvec(x) printf("vec "#x" = ");for (auto _v : x)printf("%d ",_v);puts("")
#define outtag(x) puts("----------"#x"----------")
#define outarr(a,L,R) printf(#a"[%d...%d] = ",L,R);\
						For(_v2,L,R)printf("%d ",a[_v2]);puts("");
using namespace std;
typedef long long LL;
LL read(){
	LL x=0,f=0;
	char ch=getchar();
	while (!isdigit(ch))
		f|=ch=='-',ch=getchar();
	while (isdigit(ch))
		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}
const int N=405;
int T,n,m;
int a[N][N];
int p[N],id[N],cnt[N];
bool cmp(int a,int b){
	return p[a]<p[b];
}
int Getnxt(int i,int &p){
	while (!a[i][p]&&p<=m)
		p++;
	return p<=m;
}
void solve(){
	n=read(),m=read();
	clr(a),clr(p);
	For(i,1,n)
		For(j,1,m)
			a[i][j]=read();
	For(i,1,n)
		Getnxt(i,p[i]=1);
	while (1){
		clr(cnt);
		int flag=0;
		For(i,1,n){
			cnt[a[i][p[i]]]++,id[i]=i;
			if (cnt[a[i][p[i]]]>1)
				flag=1;
		}
		if (!flag)
			break;
		sort(id+1,id+n+1,cmp);
		For(i,1,n)
			if (cnt[a[id[i]][p[id[i]]]]>1){
				if (!Getnxt(id[i],++p[id[i]])){
					puts("\(^o^)/");
					return;
				}
				break;
			}
	}
	For(i,1,n)
		printf("%d ",a[i][p[i]]);
	puts("");
}
int main(){
	T=read();
	while (T--)
		solve();
	return 0;
}

  

posted @ 2019-03-11 20:51  zzd233  阅读(231)  评论(0编辑  收藏  举报