CodeForces 1067E Random Forest Rank

题意

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

\(\texttt{Data Range:}1\leq n\leq 5\times 10^5\)

题解

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

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

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

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

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

\[\det A=\sum\limits_{\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\n",(li)res*(c+c)%MOD);
}
posted @ 2020-10-28 11:07  Karry5307  阅读(268)  评论(0编辑  收藏  举报