Loading [MathJax]/jax/output/HTML-CSS/jax.js


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' ,那么一定有: SS

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

 

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

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

  

posted @   zzd233  阅读(237)  评论(0编辑  收藏  举报
编辑推荐:
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
阅读排行:
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!

点击右上角即可分享
微信分享提示