题意
给定一棵 (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),根据秩零化度定理即可得出。于是我们转为考察邻接矩阵的行列式,这里直接考虑定义:
注意到,(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);
}