根節點不一定是1,但是是乙個確定的點,看誰不是兒子就行了。。
這道題我們考慮從兒子推到根。設f[i][j]表示以i為根的子樹中,最長輕鏈長度為j的概率。
因為每乙個son被選為重兒子的概率相同,且重兒子對父親貢獻和輕兒子不同,所以要每乙個點為重兒子,之後挨個列舉每個兒子。這個效率是n^2,然後要列舉鏈的長度,如果列舉到size[root],相當於n^3,廢掉了。。但只要列舉到size[son]+1就好了。更大不會再有意義,概率一定為零。
在計算答案時要再次用到f陣列,所以要先存一下,再轉過去。
對於每一次枚舉重兒子時列舉所有兒子時的計算方法。g[i][j]表示i節點子樹中最長鏈長為0~j的概率之和。
f[i][j]=f[son][j]*g[i][j]+g[son][j]*f[i][j]-f[i][j]*f[son][j];(對於重兒子)
考慮一次向答案上新增乙個子節點。那麼父節點最長為j的鏈出現地方有兩種可能。1,出現在之前已經新增到答案中的子節點裡(f[i][j]),那麼這時新新增的子節點的長度只要在0~j中哪乙個都無所謂了。同理,最長j出現在新新增的節點中,那之前新增的多長也就無所謂了。。。
那麼對於輕兒子呢?不同於重兒子,輕兒子與父親的連邊會對答案造成貢獻,所以只要把son的j改成j-1即可。
最後統計答案:根節點子樹中最長輕鏈長為j的概率*j,加個和就好了。
對於除法,乘逆元即可。
注:最好別看我**,巨醜,還難理解。。。我把f和g陣列合二為一了。。。。
#pragma gcc optimize("o3")
#include
#include
#include
#include
#include
#define n 3005
#define mod 1000000007
#define ll long long
using namespace std;
int read()
while(x>='0'&&x<='9')
return
sum*f;
}struct roadlu[n*2];
int n,e,adj[n];
ll out[n],sz[n],g[n],h[n],f[n][n];
bool v[n];
inline ll cheng(ll x,int m)
return ans;
}inline void dfs(int x)
ll fm=cheng(out[x],mod-2);
for(int i=adj[x];i;i=lu[i].next)
}g[0]=h[0];h[0]=0;
for(int k=1;k<=sz[to]+1;k++)g[k]=(g[k-1]+h[k])%mod,h[k]=0;
}for(int j=sz[x];j>=1;j--)g[j]=(g[j]-g[j-1]+mod)%mod;
for(int j=0;j<=sz[x];j++)f[x][j]=(f[x][j]+g[j]*fm%mod)%mod;
}if(!adj[x])f[x][0]=1;
for(int i=1;i<=n;i++)f[x][i]=(f[x][i]+f[x][i-1])%mod;
}int main()
;adj[i]=e;}}
int root;
for(int i=1;i<=n;i++)if(!v[i])root=i;
dfs(root);
ll ans=0;
for(ll i=1;i<=n;i++)
ans=(ans+i*(f[root][i]-f[root][i-1]+mod)%mod)%mod;
cout<
}
bzoj 4147 Abs 熟練剖分
題目描述 給定一棵樹,設計資料結構支援以下操作 1 u v d 表示將路徑 u,v 加d 2 u v 表示詢問路徑 u,v 上點權絕對值的和 輸入 第一行兩個整數n和m,表示結點個數和運算元 接下來一行n個整數a i,表示點i的權值 接下來n 1行,每行兩個整數u,v表示存在一條 u,v 的邊 接下...
樹鏈剖分 樹鏈剖分講解
好了,這樣我們就成功解決了對樹上修改查詢邊權或點的問題。下面放上 vector v maxn int size maxn dep maxn val maxn id maxn hson maxn top maxn fa maxn 定義 int edge 1,num 1 struct tree e ma...
樹鏈剖分 樹剖換根
這是一道模板題。給定一棵 n 個節點的樹,初始時該樹的根為 1 號節點,每個節點有乙個給定的權值。下面依次進行 m 個操作,操作分為如下五種型別 換根 將乙個指定的節點設定為樹的新根。修改路徑權值 給定兩個節點,將這兩個節點間路徑上的所有節點權值 含這兩個節點 增加乙個給定的值。修改子樹權值 給定乙...