分析
本题初看没有任何思路
但经过长达1个小时(有点夸张)的端详
我们可以注意到
对于一个图中所说的包含部分叶子的虚树
上边的节点分为两类
- 作为一条边经过,即:当前被染色,并且染了他的一个儿子
- 作为一个
LCA
经过,即:当前节点被染色,并且它的两个及以上节点被染色
为什么我们需要分开1个儿子和两个及以上儿子呢?
因为第一种情况无法作为当前虚树的根节点
反之,第二种情况就是可以的
当然,对于整棵树来说,就还有一种类型的节点
- 没有被染色
想到这里,剩下的工作也就不太麻烦了,只是转移非常的细节
我们设DP[][0]
表示上边的第三种情况DP[][1]
表示上边的第一种情况DP[][2]
表示上边的第二种情况
所以我们可以得到以下的转移方程式
至于为什么最后一个转移中会有DP[v][2]*2
呢?
因为这个点,可能是和当前节点一个虚树上的,也可能是另一棵虚树上的
代码
//CF1146F #include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <cmath> #define LL long long #define Cl(X,Y) memset((X),(Y),sizeof(X)) #define FOR(i,A,B) for(LL i=A;i<=B;i++) #define BOR(i,A,B) for(LL i=A;i>=B;i--) #define Lowbit(X) (X & (-X)) #define Skip cout<<endl; #define INF 0x3f3f3f3f3f3f3f3f #define Mod 998244353 #define Rson (X<<1|1) #define Lson (X<<1) using namespace std; const LL MaxN=2e5+10; LL DP[MaxN][3]; LL Total,u,v,w,D[MaxN]; LL Next[MaxN<<1],Head[MaxN],End[MaxN<<1],Cur; inline void File() { freopen(".in","r",stdin); freopen(".out","w",stdout); } inline void DFS(LL New,LL Pre) { if(D[New]==1 && New!=1) { DP[New][2]=1; } else { DP[New][0]=1;} for(LL i=Head[New];i;i=Next[i]) { if(End[i]==Pre) continue; DFS(End[i],New); LL Zero=DP[New][0],One=DP[New][1],Two=DP[New][2]; DP[New][0]=Zero*((DP[End[i]][0]+DP[End[i]][2])%Mod)%Mod; (DP[New][1]=One*((DP[End[i]][0]+DP[End[i]][2])%Mod)%Mod+Zero*((DP[End[i]][1]+DP[End[i]][2])%Mod)%Mod)%=Mod; (DP[New][2]=One*((DP[End[i]][1]+DP[End[i]][2])%Mod)%Mod+Two*((DP[End[i]][0]+DP[End[i]][1]+2LL*DP[End[i]][2]%Mod)%Mod)%Mod)%=Mod; } } inline void Add_Edge(LL From,LL To) { Next[++Cur]=Head[From]; Head[From]=Cur; End[Cur]=To; } signed main() { // File(); ios::sync_with_stdio(false); cin>>Total; FOR(i,2,Total) { cin>>u; Add_Edge(u,i); Add_Edge(i,u); D[u]++; D[i]++; } DFS(1,0); // FOR(i,1,Total) // cout<<"i: "<<DP[i][0]<<" "<<DP[i][1]<<" "<<DP[i][2]<<endl; cout<<(DP[1][0]+DP[1][2])%Mod<<endl; // fclose(stdin); // fclose(stdout); system("pause"); return 0; }