zoukankan      html  css  js  c++  java
  • CodeForces 1067E Random Forest Rank

    题意

    给定一棵 (n) 个节点的树,每条边有 (frac{1}{2}) 的概率出现,这样会得出一个森林,求这个森林的邻接矩阵 (A) 的秩 (operatorname{rank} A) 的期望。

    ( exttt{Data Range:}1leq nleq 5 imes 10^5)

    题解

    好题,正解是线性代数 + 期望 DP。其实是不知道结论感觉挺难只要知道结论就是 sb 题的题

    首先来证明一个结论:一个森林邻接矩阵的秩为该森林最大匹配数目的两倍。

    考虑 (operatorname{rank} A) 的一个定义,也即非零子式的最高阶数。所以,设 (operatorname{rank}A=k),我们只需要考虑一个 (k) 阶子式即可。很明显由于原图的邻接矩阵是对称矩阵,所以我们自然想去考虑一个对称的 (k) 阶子式,因为这个东西对应原图的一个导出子图。

    同时,森林的导出子图还是森林,所以类似于二分,将最优化转化为判定,只要考虑一个森林的邻接矩阵什么情况下是满秩的即可。

    注意到,对于 (n imes n) 的方阵 (A) 来说,(operatorname{rank} A=n)(det A eq 0) 等价。因为如果 (det A eq 0) 的话这个矩阵一定不是“压缩扁平化”的变换,也即 (dim operatorname{Ker} A=0),根据秩零化度定理即可得出。于是我们转为考察邻接矩阵的行列式,这里直接考虑定义:

    [det A=sumlimits_{pi}prod_{i=1}^{n}A_{i,pi_i} ]

    注意到,(A_{i,pi_i}) 非零,表示图上存在一条 ((i,pi_i)) 的边。所以 (det A) 非零当且仅当存在一个排列 (pi) 使得 ((i,pi_i)) 存在边。

    将这个排列写成置换的形式,可以分解成若干个循环。注意到考察的对象是森林,所以所有循环的长度只可能是 (2),也即一条边产生一个循环。

    这个时候,可以将每个循环内的点两两匹配,很明显的,由于每个点都被匹配上了,这个匹配是完美匹配。

    于是我们推出一个结论:对于一个森林的邻接矩阵 (A) 来说,(operatorname{rank} A=n) 与森林有完美匹配是等价的。所以 (operatorname{rank} A) 也就是原图的最大满足有完美匹配的导出子图的大小。考虑求出原图的一个最大匹配,因为没有比最大匹配更大的匹配,所以我们可以选原图的最大匹配作为导出子图。

    通过上述的说明,我们成功证明了 (operatorname{rank} A) 为最大匹配数目的两倍。

    这个时候就变成求最大匹配数目的期望了。根据期望的线性性,设 (f_u) 表示 (u) 与一个孩子匹配的概率,那么答案为所有 (f_u) 的和。同时这个 (f_u) 的转移非常简单,就不做过多说明了。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef int ll;
    typedef long long int li;
    const ll MAXN=5e5+51,MOD=998244353,INV2=499122177;
    struct Edge{
        ll to,prev;
    };
    Edge ed[MAXN<<1];
    ll n,tot,from,to,res,c;
    ll last[MAXN],f[MAXN];
    namespace FastIO{
        char buf[MAXN*5],*st=buf,*ed=buf;
        inline char gc()
        {
            return st==ed&&(ed=(st=buf)+fread(buf,1,2500000,stdin),ed==st)?0:*st++; 
        }
    }
    using FastIO::gc;
    inline ll read()
    {
        register ll num=0;
        register char ch=gc();
        while(!isdigit(ch)&&ch!='-')
        {
            ch=gc();
        }
        while(isdigit(ch))
        {
            num=(num<<3)+(num<<1)+(ch-'0');
            ch=gc();
        }
        return num;
    }
    inline void addEdge(ll from,ll to)
    {
        ed[++tot].prev=last[from];
        ed[tot].to=to;
        last[from]=tot;
    }
    inline void dfs(ll node,ll fa)
    {
        ll prod=1;
        for(register int i=last[node];i;i=ed[i].prev)
        {
            if(ed[i].to!=fa)
            {
                dfs(ed[i].to,node),prod=(li)prod*(1+f[ed[i].to])%MOD*INV2%MOD;
            }
        }     
        res=(res+(f[node]=MOD+1-prod))%MOD;
    }
    int main()
    {
        n=read(),c=1;
        for(register int i=0;i<n-1;i++)
        {
            from=read(),to=read(),addEdge(from,to),addEdge(to,from),c=(c+c)%MOD;
        }
        dfs(1,0),printf("%d
    ",(li)res*(c+c)%MOD);
    }
    
  • 相关阅读:
    CentOS 6.6 系统升级到 CentOS 6.7
    Nginx 默认的日志类型
    windows 系统后台运行 jar 包
    windows 下启动运行 jar 包程序
    Zabbix 添加端口监控链接
    提取 linux 文件目录结构
    Android LayoutInflater详解
    String,StringBuffer与StringBuilder的区别??
    Android中Cursor类的概念和用法
    Intent中的四个重要属性——Action、Data、Category、Extras
  • 原文地址:https://www.cnblogs.com/Karry5307/p/13889856.html
Copyright © 2011-2022 走看看