UVA11174村民排队问题
生活随笔
收集整理的這篇文章主要介紹了
UVA11174村民排队问题
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
題意:
? ? ?有n個(gè)人要排隊(duì),給你一些父子關(guān)系,要求兒子不能站在自己的父親前面,問有多少種排隊(duì)方式?
思路:
? ? ? 白書上的題目,首先我們可以把關(guān)系建成樹,這樣我們就有可能得到一個(gè)森林(或者是一課樹),然后我們再虛擬出來一個(gè)點(diǎn)0連接所有森林的根節(jié)點(diǎn),這樣是為了保證是一棵樹,然后題目就變成了給你一棵樹,不改變關(guān)系,問這個(gè)樹有多少種方式,這個(gè)還是排列組合問題,對于每一個(gè)根節(jié)點(diǎn),有這樣的性質(zhì)
root[i] = f[1]*f[2]*..f[k] ? * ?(s[i]-1)!/s[1]!*s[2]!*..s[k]!?
f[1]..f[k]表示的當(dāng)前根節(jié)點(diǎn)連接的k個(gè)兒子為根節(jié)點(diǎn)的樹的排列個(gè)數(shù),s[i]表示的是以i為根節(jié)點(diǎn)的這棵樹的所有節(jié)點(diǎn)個(gè)數(shù),上面的式子可以理解成這樣
f[1]*f[2]*..f[k] 所有兒子為根節(jié)點(diǎn)的排列個(gè)數(shù)
(s[i] - 1)! 表示的是以i為根的這棵樹的所有節(jié)點(diǎn)數(shù)-1(不算跟所以-1)的排列方式
//s[1]!*s[2]!*..s[k]! ?相當(dāng)于全排列去掉重復(fù)部分,因?yàn)槊恳豢脴湟呀?jīng)*f[]了,不能在*了,不能再*那就可以理解成重復(fù)部分了,所以...,然后把每一個(gè)公式都化簡,會(huì)發(fā)現(xiàn)分子剩1,分母剩s[i]了,(可以理解成分子的f[i]和分母的s[i]!約),這樣最后剩下的是
((n+1)-1)/s1*s2*..sn;
最后的公式是
Ans = n!/(s1*s2*s3*s4...*sn) si是以i為根節(jié)點(diǎn)的子樹的節(jié)點(diǎn)數(shù)
這樣就可以了,然后這樣會(huì)設(shè)計(jì)到一個(gè)問題,那就是大數(shù)相除取余的問題,我知道應(yīng)該有至少兩種方法解決這個(gè)問題,一個(gè)是逆元,另一個(gè)是a/b%c = a * pow(b ,c - 2) % c,這個(gè)不解釋了,可以自己去網(wǎng)上找學(xué)習(xí),哎!想起了亞洲賽那道排列組合題,當(dāng)時(shí)在賽場上怎么也想不起來大數(shù)相除的轉(zhuǎn)換了,sb了!
#include<stdio.h>
#include<string.h>
#define MOD 1000000007
#define N_node 40000 + 10
#define N_edge 40000 + 10
typedef struct
{
? ?int to ,next;
}STAR;
STAR E[N_node];
long long sum[N_node];
long long jc[N_node];
int list[N_node] ,tot;
int du[N_node];
void add(int a ,int b)
{
? ?E[++tot].to = b;
? ?E[tot].next = list[a];
? ?list[a] = tot;
}
int DFS(int now)
{
? ?int s = 1;
? ?for(int k = list[now] ;k ;k = E[k].next)
? ?{
? ? ? s += DFS(E[k].to);
? ?}
? ?sum[now] = s;
? ?return s;
}
long long Pow(long long a ,long long b)
{
? ?long long c = 1;
? ?while(b)
? ?{
? ? ? if(b & 1) c *= a;
? ? ? b >>= 1;
? ? ? a *= a;
? ? ? a = a % MOD;
? ? ? c = c % MOD;
? ?}
? ?return c;
}
void DB()
{
? ?long long now = 1;
? ?for(long long i = 1 ;i <= 40000 ;i ++)
? ?{
? ? ? now = now * i % MOD;
? ? ? jc[i] = now;
? ?}
}
? ?
int main ()
{
? ?int n ,m ,i ,a ,b ,t;
? ?DB();
? ?scanf("%d" ,&t);
? ?while(t--)
? ?{
? ? ? scanf("%d %d" ,&n ,&m);
? ? ? memset(list ,0 ,sizeof(list)) ,tot = 1;
? ? ? memset(du ,0 ,sizeof(du));
? ? ? for(i = 1 ;i <= m ;i ++)
? ? ? {
? ? ? ? ?scanf("%d %d" ,&a ,&b);
? ? ? ? ?add(b ,a);
? ? ? ? ?du[a] ++;
? ? ? }
? ? ? for(i = 1 ;i <= n ;i ++)
? ? ? if(!du[i]) add(0 ,i);
? ? ? memset(sum ,0 ,sizeof(sum));
? ? ? DFS(0);
? ? ? long long Ans = jc[n];
? ? ? for(i = 1 ;i <= n ;i ++)
? ? ? {
? ? ? ? ? Ans = Ans * Pow(sum[i] ,MOD - 2) % MOD;
? ? ? }
? ? ? printf("%lld\n" ,Ans);
? ?}
? ?return 0;
}
? ? ??
? ? ??
? ? ??
? ?
? ? ??
? ? ?有n個(gè)人要排隊(duì),給你一些父子關(guān)系,要求兒子不能站在自己的父親前面,問有多少種排隊(duì)方式?
思路:
? ? ? 白書上的題目,首先我們可以把關(guān)系建成樹,這樣我們就有可能得到一個(gè)森林(或者是一課樹),然后我們再虛擬出來一個(gè)點(diǎn)0連接所有森林的根節(jié)點(diǎn),這樣是為了保證是一棵樹,然后題目就變成了給你一棵樹,不改變關(guān)系,問這個(gè)樹有多少種方式,這個(gè)還是排列組合問題,對于每一個(gè)根節(jié)點(diǎn),有這樣的性質(zhì)
root[i] = f[1]*f[2]*..f[k] ? * ?(s[i]-1)!/s[1]!*s[2]!*..s[k]!?
f[1]..f[k]表示的當(dāng)前根節(jié)點(diǎn)連接的k個(gè)兒子為根節(jié)點(diǎn)的樹的排列個(gè)數(shù),s[i]表示的是以i為根節(jié)點(diǎn)的這棵樹的所有節(jié)點(diǎn)個(gè)數(shù),上面的式子可以理解成這樣
f[1]*f[2]*..f[k] 所有兒子為根節(jié)點(diǎn)的排列個(gè)數(shù)
(s[i] - 1)! 表示的是以i為根的這棵樹的所有節(jié)點(diǎn)數(shù)-1(不算跟所以-1)的排列方式
//s[1]!*s[2]!*..s[k]! ?相當(dāng)于全排列去掉重復(fù)部分,因?yàn)槊恳豢脴湟呀?jīng)*f[]了,不能在*了,不能再*那就可以理解成重復(fù)部分了,所以...,然后把每一個(gè)公式都化簡,會(huì)發(fā)現(xiàn)分子剩1,分母剩s[i]了,(可以理解成分子的f[i]和分母的s[i]!約),這樣最后剩下的是
((n+1)-1)/s1*s2*..sn;
最后的公式是
Ans = n!/(s1*s2*s3*s4...*sn) si是以i為根節(jié)點(diǎn)的子樹的節(jié)點(diǎn)數(shù)
這樣就可以了,然后這樣會(huì)設(shè)計(jì)到一個(gè)問題,那就是大數(shù)相除取余的問題,我知道應(yīng)該有至少兩種方法解決這個(gè)問題,一個(gè)是逆元,另一個(gè)是a/b%c = a * pow(b ,c - 2) % c,這個(gè)不解釋了,可以自己去網(wǎng)上找學(xué)習(xí),哎!想起了亞洲賽那道排列組合題,當(dāng)時(shí)在賽場上怎么也想不起來大數(shù)相除的轉(zhuǎn)換了,sb了!
#include<stdio.h>
#include<string.h>
#define MOD 1000000007
#define N_node 40000 + 10
#define N_edge 40000 + 10
typedef struct
{
? ?int to ,next;
}STAR;
STAR E[N_node];
long long sum[N_node];
long long jc[N_node];
int list[N_node] ,tot;
int du[N_node];
void add(int a ,int b)
{
? ?E[++tot].to = b;
? ?E[tot].next = list[a];
? ?list[a] = tot;
}
int DFS(int now)
{
? ?int s = 1;
? ?for(int k = list[now] ;k ;k = E[k].next)
? ?{
? ? ? s += DFS(E[k].to);
? ?}
? ?sum[now] = s;
? ?return s;
}
long long Pow(long long a ,long long b)
{
? ?long long c = 1;
? ?while(b)
? ?{
? ? ? if(b & 1) c *= a;
? ? ? b >>= 1;
? ? ? a *= a;
? ? ? a = a % MOD;
? ? ? c = c % MOD;
? ?}
? ?return c;
}
void DB()
{
? ?long long now = 1;
? ?for(long long i = 1 ;i <= 40000 ;i ++)
? ?{
? ? ? now = now * i % MOD;
? ? ? jc[i] = now;
? ?}
}
? ?
int main ()
{
? ?int n ,m ,i ,a ,b ,t;
? ?DB();
? ?scanf("%d" ,&t);
? ?while(t--)
? ?{
? ? ? scanf("%d %d" ,&n ,&m);
? ? ? memset(list ,0 ,sizeof(list)) ,tot = 1;
? ? ? memset(du ,0 ,sizeof(du));
? ? ? for(i = 1 ;i <= m ;i ++)
? ? ? {
? ? ? ? ?scanf("%d %d" ,&a ,&b);
? ? ? ? ?add(b ,a);
? ? ? ? ?du[a] ++;
? ? ? }
? ? ? for(i = 1 ;i <= n ;i ++)
? ? ? if(!du[i]) add(0 ,i);
? ? ? memset(sum ,0 ,sizeof(sum));
? ? ? DFS(0);
? ? ? long long Ans = jc[n];
? ? ? for(i = 1 ;i <= n ;i ++)
? ? ? {
? ? ? ? ? Ans = Ans * Pow(sum[i] ,MOD - 2) % MOD;
? ? ? }
? ? ? printf("%lld\n" ,Ans);
? ?}
? ?return 0;
}
? ? ??
? ? ??
? ? ??
? ?
? ? ??
總結(jié)
以上是生活随笔為你收集整理的UVA11174村民排队问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UVA11137(立方数之和)
- 下一篇: UVA11991第k次出现的v的下标