1. 程式人生 > >NOI2018湖北省隊集訓Day5 T1 tree

NOI2018湖北省隊集訓Day5 T1 tree

題面:
這裡寫圖片描述

得分情況:
寫了樸素的O(n2)dp沒看到OJ上有多組資料,成功爆零了。

正解:
首先O(n2)的樸素dp很簡單,dp[i]表示在以i為根的子樹中滿足條件的方案數,轉移是dp[i]=j(ji)(dp[j]+1,一次的時間複雜度為O(n),然後以每個點為根分別做一次dp就行了。
很明顯這題是要換根dp,首先我們跑一遍以1為根的dp,然後從1往下進行第二遍dfs,每個點需要乘的是他的父親除了他這個兒子以外的其他兒子的dp值+1相乘,維護字首積和字尾積即可。總複雜度為O(n

)

程式碼:

#include <bits/stdc++.h>
using namespace std;

const long long mod=1e9+7;
const int maxn=1e5+100;
long long dp[maxn],g[maxn];
int n,T;
vector <int> map1[maxn];

void dfs1(int now)
{
    dp[now]=1;
    for(int i=0;i<map1[now].size();i++)
    {
        int to=map1[now][i];
        dfs1(to);
        dp[now]=(dp[now]*(dp[to]+1
))%mod; } } void dfs2(int now) { vector <int> l,r; int num=map1[now].size(); for(int i=0;i<=num+1;i++) { l.push_back(0); r.push_back(0); } l[0]=1; for(int i=0;i<num;i++) { int to=map1[now][i]; l[i+1]=(l[i]*(dp[to]+1))%mod; } r[num+1
]=1; for(int i=num-1;i>=0;i--) { int to=map1[now][i]; r[i+1]=(r[i+2]*(dp[to]+1))%mod; } int k=0; for(int i=0;i<num;i++) { k++; g[map1[now][i]]=(((g[now]*l[k-1])%mod*r[k+1])%mod+1)%mod; dfs2(map1[now][i]); } } int main() { scanf("%d",&T); while(T--) { scanf("%d",&n); for(int i=1;i<=n;i++) map1[i].clear(); for(int i=2;i<=n;i++) { int x; scanf("%d",&x); map1[x].push_back(i); } dfs1(1); g[1]=1; dfs2(1); for(int i=1;i<=n;i++) { printf("%lld ",dp[i]*g[i]%mod);} printf("\n"); } return 0; }