Official
Ex - Perfect Binary Tree Editorial by physics0523
重要な観察として、以下が挙げられます。
- 深さ \(20\) 以上の完全二分木が誘導されることはない。
- 理由: 深さが \(20\) 以上の完全二分木が誘導されるためには \(2^{20}-1 = 1048575\) 個以上の頂点が必要になりますが、制約上頂点は \(3 \times 10^5\) 個しかないので、不足します。
さらに、ここから以下のことも言えます。
- 深さ \(20\) 以上にある頂点は無視してもよい。
これにより、例えば \(N\) 頂点全てについて、その頂点が追加された際に祖先全てに関して何らかの更新を行う (高々 \(20\) 回) といった解法が成立するという考察が立てられます。
では、本問題を解いていきましょう。以下の値を管理していくことを考えます。
- \(dp[\) 頂点 \(v\) \(][\) 深さ \(d\) \(]\) … 頂点 \(v\) を根とした時に誘導される深さ \(d\) (頂点数 \(2^{d+1}-1\)) の完全二分木の数
- \(csum[\) 頂点 \(v\) \(][\) 深さ \(d\) \(]\) … \(v\) の子 \(w\) 全てに対する \(dp[w][d]\) の総和
ここで、便宜上頂点 \(1\) の親として頂点 \(0\) があるものとして取り扱うと後々便利です。
最初は頂点 \(1\) のみがある状態から始めて、頂点 \(2\) 以降を順次追加して答えを求めていくことを考えます。
なお、各段階での答えは \(csum[0]\) の総和であることに留意してください。
ある頂点 \(v\) を追加した際に、以下を行っていけばよいです。
- \(v\) が深さ \(20\) 以上の位置にあるなら何も更新する必要がない
- そうでない場合、 \(v\) とその祖先全てに対して \(dp,csum\) を更新する
- 具体的には、 \(v\) を含む完全二分木の数を各深さについて数えて加算していく。
- 完全二分木の深さをひとつ増やす際、 \(v\) を含む側の完全二分木として考えられるものの数を保持しつつ、もう片方の完全二分木として考えられるものの数 ( \(dp,csum\) から求められます) を掛けていけばよいです。
実装例(C++):
#include<bits/stdc++.h>
#define mod 998244353
using namespace std;
int main(){
int n;
cin >> n;
vector<int> p(n+1);
for(int i=2;i<=n;i++){cin >> p[i];}
vector<int> d(n+1,0);
vector<vector<long long>> dp(n+1,vector<long long>(20,0));
vector<vector<long long>> csum(n+1,vector<long long>(20,0));
p[1]=0;
dp[1][0]=1;
csum[0][0]=1;
cout << "1\n";
for(int i=2;i<=n;i++){
d[i]=d[p[i]]+1;
if(d[i]<20){
long long val=1;
int v,curd;
v=i;curd=0;
while(1){
int par=p[v];
long long nval=val*(mod+csum[par][curd]-dp[v][curd])%mod;
csum[par][curd]+=val;csum[par][curd]%=mod;
dp[v][curd]+=val;dp[v][curd]%=mod;
if(par==0){break;}
v=par;
curd++;
val=nval;
}
}
long long res=0;
for(int i=0;i<20;i++){
res+=csum[0][i];res%=mod;
}
cout << res << "\n";
}
return 0;
}
posted:
last update: