Codeforces 1067E - Random Forest Rank(找性质+树形 dp)
一道不知道能不能算上自己 AC 的 D1E(?)
挺有意思的结论题,结论倒是自己猜出来了,可根本不会证(
开始搬运题解 ing:
碰到这样的题我们肯定要考虑一个图邻接矩阵的秩是什么。显然根据我们幼儿园就学过的线性代数,对于一个矩阵 \(A\) 而言,其行列式就是其最大的子式满足其行列式不等于 \(0\),也就是任取若干行 & 若干列,它们的交组成的矩阵行列式不等于 \(0\),不难发现对于一个森林的邻接矩阵而言,对于任意一个子式,如果它的行列式不等于 \(0\),那么它的行、列下标的并组成的主子式的行列式肯定也非零,因此我们只用考虑其主子式就行了,而主子式显然可以看作导出子图的邻接矩阵,因此我们接下来只需探究什么样的矩阵行列式非零。
考虑行列式的展开式:
显然对于后面的 \(\prod\) 而言,只有 \((i,p_i)\) 存在边相连时 \(A_{i,p_i}\) 才非零,而由于图无自环,如果 \(\exists i,s.t.i=p_i\) 必然有 \(p\) 对 \(\det A\) 的贡献为 \(0\),同理,如果 \(p\) 存在一个大小 \(\ge 3\) 的置换环,那么由于图是一个森林,不可能这些点之间两两相邻的点都存在边,对 \(\det A\) 的贡献也为 \(0\)。也就是说只有 \(p\) 中所有置换环大小都为二,也就是我们选出的是一个形如的 \((i,p_i)\) 的完美匹配,而由于图是一个森林,因此完美匹配个数肯定 \(\le 1\),具体构造方案就是从叶子开始贪心地匹配,如果匹配不了就不存在完美匹配,因此 \(\det A\) 非零的充要条件是图存在完美匹配,因此我们可以得到这样一个性质:
Observation. 对于图 \(G\) 而言,\(G\) 邻接矩阵的秩是 \(G\) 最大匹配的大小 \(\times 2\)。
有了这个性质之后就可以 DP 了。\(way_{i,0/1}\) 表示钦定以 \(i\) 为根的子树内的边,并且在最大匹配中 \(i\) 选/没选的方案数,\(dp_{i,0/i}\) 表示钦定以 \(i\) 为根的子树内的边,并且在最大匹配中 \(i\) 选/没选的所有方案中最大匹配个数的总和。转移就分 \((i,j)\) 的边选/不选,然后分类讨论一下即可,复杂度 \(\mathcal O(n)\)
const int MAXN=5e5;
const int MOD=998244353;
int n,hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int dp[MAXN+5][2],way[MAXN+5][2];
void dfs(int x,int f){
way[x][0]=1;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f) continue;dfs(y,x);
static int tmp_dp[2]={0},tmp_way[2]={0};
memset(tmp_dp,0,sizeof(tmp_dp));
memset(tmp_way,0,sizeof(tmp_way));
tmp_way[0]=(1ll*way[x][0]*(way[y][0]+way[y][1]))%MOD;
tmp_dp[0]=(1ll*dp[x][0]*(way[y][0]+way[y][1])+1ll*(dp[y][0]+dp[y][1])*way[x][0])%MOD;
tmp_way[1]=(1ll*way[x][1]*(way[y][0]+way[y][1])*2)%MOD;
tmp_dp[1]=2ll*(1ll*dp[x][1]*(way[y][0]+way[y][1])+1ll*(dp[y][0]+dp[y][1])*way[x][1])%MOD;
tmp_way[0]=(tmp_way[0]+1ll*way[x][0]*way[y][1])%MOD;
tmp_dp[0]=(tmp_dp[0]+1ll*dp[x][0]*way[y][1]+1ll*dp[y][1]*way[x][0])%MOD;
tmp_way[1]=(tmp_way[1]+1ll*way[x][0]*way[y][0])%MOD;
tmp_dp[1]=(tmp_dp[1]+1ll*dp[x][0]*way[y][0]+1ll*dp[y][0]*way[x][0]+1ll*way[x][0]*way[y][0])%MOD;
for(int i=0;i<2;i++) dp[x][i]=tmp_dp[i],way[x][i]=tmp_way[i];
}
// printf("%d %d %d %d %d\n",x,dp[x][0],dp[x][1],way[x][0],way[x][1]);
}
int main(){
scanf("%d",&n);
for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),adde(u,v),adde(v,u);
dfs(1,0);printf("%d\n",2ll*(dp[1][0]+dp[1][1])%MOD);
return 0;
}