【UOJ#75】【UR #6】智商锁(矩阵树定理,随机)

本文探讨了矩阵树定理在随机图生成树计数中的应用,通过构造特定无向图并利用矩阵树定理计算生成树个数,解决了一个复杂的问题。文章详细介绍了算法流程,包括随机图的生成、生成树计数、哈希表应用以及概率覆盖的数学证明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【UOJ#75】【UR #6】智商锁(矩阵树定理,随机)

题面

UOJ

题解

这种题我哪里做得来啊[惊恐],,,
题解做法:随机\(1000\)个点数为\(12\)的无向图,矩阵树定理算出它的生成树个数,然后找到四张图不拼接直接放在一起,也就是找到四个图,假设其生成树个数是\(f(G)\),那么就找到\(f(G_1)f(G_2)f(G_3)f(G_4)\equiv k\),然后预处理两两的乘积,丢到哈希表/\(\text{map}\)里,枚举另外一半直接查。。。
无向图的生成方式是每条边出现的概率是\(0.8\)。然后\(rand\)的时候确保图连通。
还需要再特判一下\(k=0\)的情况。

然后概率的证明直接蒯题解吧。。。

首先我们随机无向图的方法是每一条边生成的概率都是\(0.8\),而\(12\)个点的完全图的生成树个数是\(12^{10}\),这是远大于模数的,所以我们可以近似地把\(f_i\)看做均匀分布的随机数(这个和我们平时写的哈希表的思想类似)。接下来我们把这\(1000\)个随机数的集合四次方再模上模数,这样我们就得到了\(10^{12}\)个数,和刚才同理,我们也可以把这\(10^{12}\)个数给近似地看成均匀分布的随机数。

接下来我们要求的是\(10^{12}\)个小于等于\(10^9\)的随机数完全覆盖\(0\)\(10^9\)之间所有数的概率是多少。首先一个数被覆盖的概率是\(1-(1-\frac{1}{10^9})^{10^{12}} \approx 1-\mathrm{e}^{-10^3}\),我们可以近似地认为所有数都被覆盖的概率是\((1-\mathrm{e}^{-10^3})^{10^9}\),实际上这个数是非常接近\(1\)的(因为\(e^{10^3}\)\(10^9\)大到哪里去都不知道了)

打不过打不过。

#include<iostream>
#include<cstdio>
#include<map>
using namespace std;
#define MOD 998244353
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int fpow(int a,int b){int s=1;while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}return s;}
struct Graph
{
    int g[15][15],a[15][15],T,edge;
    void Calc()
    {
        T=1;
        for(int i=2;i<=12;++i)
        {
            for(int j=i+1;j<=12;++j)
                while(a[j][i])
                {
                    int t=a[i][i]/a[j][i];
                    for(int k=i;k<=12;++k)a[i][k]=(a[i][k]+MOD-1ll*a[j][k]*t%MOD)%MOD;
                    for(int k=i;k<=12;++k)swap(a[i][k],a[j][k]);
                    T=MOD-T;
                }
        }
        for(int i=2;i<=12;++i)T=1ll*T*a[i][i]%MOD;T=(T+MOD)%MOD;
    }
    void Add(int u,int v){++edge;g[u][v]=g[v][u]=1;a[u][v]--;a[v][u]--;a[u][u]++;a[v][v]++;}
    void Rand()
    {
        for(int i=2;i<=12;++i)Add(rand()%(i-1)+1,i);
        for(int i=1;i<=12;++i)
            for(int j=i+1;j<=12;++j)
                if(!g[i][j]&&rand()%10<8)
                    Add(i,j);
        Calc();
    }
    void Output(int N)
    {
        for(int i=1;i<=12;++i)
            for(int j=i+1;j<=12;++j)
                if(g[i][j])printf("%d %d\n",N+i,N+j);
    }
}G[1010];
pair<int,int> a[1010101];
int S[1010101],tot;
map<int,pair<int,int> >M;
void Output(int a,int b,int c,int d)
{
    printf("48 %d\n",G[a].edge+G[b].edge+G[c].edge+G[d].edge+3);
    G[a].Output(0);G[b].Output(12);G[c].Output(24);G[d].Output(36);
    printf("1 13\n13 25\n25 37\n");
}
int main()
{
    for(int i=1;i<=1000;++i)G[i].Rand();
    for(int i=1;i<=1000;++i)
        for(int j=i;j<=1000;++j)
            ++tot,M[S[tot]=1ll*G[i].T*G[j].T%MOD]=a[tot]=make_pair(i,j);
    int T=read();
    while(T--)
    {
        int K=read();if(!K){puts("2 0");continue;}
        for(int i=1;i<=tot;++i)
        {
            int v=1ll*K*fpow(S[i],MOD-2)%MOD;
            if(M.find(v)!=M.end())
            {
                Output(a[i].first,a[i].second,M[v].first,M[v].second);
                break;
            }
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/cjyyb/p/11088753.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值