逆元+樹形dp-memset-hdu4661-Message Passing


題意:有一棵樹,每個節點有一個值,每操作一次,就能把當前節點擁有的值複製到另一個節點上去。經過最少的操作次數,就可以所有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

*/












發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章