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; }