題意:有一棵樹,每個節點有一個值,每操作一次,就能把當前節點擁有的值複製到另一個節點上去。經過最少的操作次數,就可以所有n個節點上擁有n個值,問方案數。
解答:
1、啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊
沒什麼我只是叫一下。
首先,最優的方案肯定是把所有n個節點的值都先複製到某一個節點上面去。
因爲假如你現在有三個值,1,2,3.現在有一個節點有1和2,另一個節點有1,最後一個節點有3.那麼最後一個節點如果複製給第一個節點,那就滿足“所有n個節點的值都先複製到某一個節點”,如果最後一個節點複製到第二個節點,那麼最後總要複製一次到節點1上的,所以“所有n個節點的值都先複製到某一個節點”肯定是最優的策略。
2、那就需要遍歷一遍所有的節點,把節點當作根節點,算出這個節點收到所有信息的方案數r,然後發送出去的方案數肯定也是r,那麼一個節點就有r^2種方案,把所有節點作爲根節點的方案數累加起來就是最終的結果。
3、那麼每個節點的方案數=孩子1的方案數×孩子2的方案數…… × (所有子樹節點的階乘/(孩子1所有子樹節點的階乘×孩子2所有子樹節點的階乘×……))
4、這樣子的話只用把一個節點作爲根節點,算出它的dp值。然後算他第一個孩子的根節點的時候,就可以把父親節點當作一個孩子節點。那麼父親節點的dp值就是除去當前節點得到的dp值。
父親節點除去當前節點的其他孩子得到的dp值 = dp【父親】 × (n-1-當前節點子樹節點的個數)! × (當前節點子樹的個數)! / (dp【當前節點】 × (n-1)!)
當前節點的方案數 = 父親節點除去當前節點的其他孩子得到的dp值 × 當前節點孩子1的方案數 × 當前節點孩子2的方案數 …… × (( n-1)的階乘/ (
(孩子1所有子樹節點的階乘×孩子2所有子樹節點的階乘×……)× (n-當前節點的子樹個數)的階乘) )
求逆元
首先,想求的肯定是(a/b)mod m的值。
(a/b)mod m 並不等於 (a mod m)/(b mod m)
問題轉換爲求(a × b^(-1) )mod m 的值
那麼只要m是質數就可以使用費馬小定理:
b^(-1) = ( b ^ (m-2) )mod m
還有一個知道b 和 m ,通過擴展歐幾里得求逆元的方法:
int extgcd(int a, int b, int& x, int& y){
int d = a;
if (b != 0){
d = extgcd(b,a%b,y,x);
y -= (a/b) *x;
}else{
x = 1; y = 0;
}return d;
}
int mod_inverse (int a, int m){
int x,y;
extgcd(a,m,x,y);
return (m + x %m) %m;
}//知道b和m。給它一個b和一個m,返回b的逆元
微軟的編譯器(C++)
#pragma comment(linker, "/STACK:102400000,102400000")
G++
int size = 256 << 20; // 256MB
char *p = (char*)malloc(size) + size;
__asm__("movl %0, %%esp\n" :: "r"(p));
話說在http://blog.csdn.net/liguan1/article/details/9925611 的博客看到了求逆元的一行寫法
在http://www.cnblogs.com/sbaof/p/3264911.html 的博客看到了求頭文件的方法
這題我用memset整個數組超時,然後改成memset部分數組/fill部分數組/for循環部分數組 都可以ac
但我以前有一道題用for循環超時,用memset數組才過的。而且memset整個數組可能預估錯誤memset少了,然後wa到死。
#pragma comment(linker, "/STACK:102400000,102400000")
//在dfs的時候會有遞歸。有遞歸就會用到c++自帶的棧。會爆棧
//#include<bits/stdc++.h>//啊我去在國內oj中,poj,hdu 不支持這個函數,其他國外的oj,還有臺灣的oj都支持,CF,Topcoder也都支持
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define SIZE_A 4000005
#define SIZE_N 1000005
#define MOD 1000000007
using namespace std;
struct pp
{
int to,next;
}pra[SIZE_A];
int e,head[SIZE_A];
int n;
void init()//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
{
e = 0;
//fill(head,head+ 2 * n + 5, -1);
/*for (int i = 0; i <= 2 * n; i++)
head[i] = -1;*/
memset(head,-1, (2* n + 5 ) * sizeof(int) );
}
void addedge(int x,int y)
{
pra[e].to = y;
pra[e].next = head[x];
head[x] = e++;
}
long long getpower(long long x, long long n)
{
long long res = 1LL;
while (n > 0){
if (n & 1) res = (res * x) % MOD;
x = (x*x) % MOD;
n >>= 1;
}
return res%MOD;
}
long long fac[SIZE_N],ff[SIZE_N],f[SIZE_N];
long long c[SIZE_N];//孩子個數
long long cc[SIZE_N];//子樹階乘的乘積
void dfs(int now, int fa)
{
c[now] = 1LL;
//vis[now] = 1;
ff[now] = 1LL;
cc[now] = 1LL;
f[now] = 1LL;
for (int i = head[now]; i != -1; i = pra[i].next){
int u = pra[i].to;
if (u != fa){ //&& vis[u] != 1){
dfs(u,now);
c[now] += c[u];
ff[now] = ff[now] * f[u] % MOD;
cc[now] = cc[now] * fac[c[u] ] % MOD;
}
}
f[now] = (ff[now] * fac[c[now]-1] )% MOD * getpower(cc[now], MOD-2)% MOD;
}
long long dpf[SIZE_N];
long long ans;
void dfsff(int now, int fa)
{
//vis[now] = 1;
long long P = MOD;
if (now != 1){
long long t = f[now] * (n-c[now]) %P * cc[now] % P;
t = getpower(t,MOD-2);
f[now] = f[fa] * ff[now] % P * fac[c[now]] %P * t % P;
long long res = f[now] * f[now] %P;
ans = (ans + res)%P;
}
else
ans = f[now] * f[now] %P;
for (int i = head[now]; i != -1; i = pra[i].next){
int u = pra[i].to;
if (u != fa )//&& vis[u]!= 1)
dfsff(u,now);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("input.txt","r",stdin);
#endif
fac[0] = 1;
fac[1] = 1;
for (int i = 2; i <= 1000000; i++)
fac[i] = ( fac[i-1] * i ) % MOD;
int T;
scanf("%d",&T);
while (T--){
scanf("%d",&n);init();
for (int i = 1; i < n; i++){
int tempx,tempy;
scanf("%d %d",&tempx,&tempy);
addedge(tempx,tempy);
addedge(tempy,tempx);
}
dfs(1,0);
//memset(vis,0,sizeof(vis));
dfsff(1,0);
printf("%I64d\n",ans);
}
return 0;
}
/*
2
2
1 2
3
1 2
2 3
*/