UOJ#41. 【清华集训2014】矩阵变换 构造
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ41.html
题解
首先写个乱搞:
初始每行选择第一个非零元素。然后不断进行如下调整,直到没有两行选择了相同的数:
任取选了相同数的两行,更新被选数较为靠前的那行,即取该行后一个非负整数。
交上去。
过了。
wtf?
然后发现证明这个结论我花的时间远远大于AC这题QAQ
现在我们来证明一下:
引理1
如果这样调整最终能够顺利结束,那么必然得到一组合法解。
证明:
首先,任意两行互不相同,所以如果解不合法,假设第 i 行选择了第 pi 个位置,那么只能存在某两行 i,j,满足 pi<pj,且第 j 行的处于 (pi,pj) 中的某个位置上的数 x 与第 i 行选择的数相同。
但是,由于第 j 行选择的 x 已经被调整掉了,所以一定存在另一行,选择了一个更靠后的 x,这与任意两行互不相同矛盾。
所以引理得证。
然后,我们来分两步证明一定有解。
引理2
假设当前状态下,我们在所有行选择的元素构成的集合为 S ;设若干次更新更新后的集合为 S' ,那么一定有: S⊆S′ 。
证明:只有在某两行选择相同的数时会更新,那么这两行至少保留一行,所以原本处于集合中的元素不会消失。
接下来我们证明一个命题。
引理3
以这样的方式,一定可以找到一组解。
证明:
首先,只有当在更新某一行时没有下一个非负整数时才找不到解。
根据引理2,我们知道如果找不到解,则必然有某个数从未出现在被选集合中。
但是每个数在每一行出现一次,
而我们不可能在不把这个元素更新掉的情况下把整一行更新空。
于是又得到矛盾,证明了结论。
结合引理1和引理3,我们一定可以找到满足合法解!
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | #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; } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!