您的位置:首页 > 其它

UVa 11174 (乘法逆元) Stand in a Line

2015-03-02 22:13 239 查看
题意:

有n个人排队,要求每个人不能排在自己父亲的前面(如果有的话),求所有的排队方案数模1e9+7的值。

分析:

《训练指南》上分析得挺清楚的,把公式贴一下吧:

设f(i)为以i为根节点的子树的排列方法,s(i)表示以i为根的子树的节点总数。

f(i) = f(c1)f(c2)...f(ck)×(s(i)-1)!/(s(c1)!s(c2)!...s(ck)!)

按照书上最开始举的例子,其实这个式子也不难理解,就是先给这些子树确定一下位置,即有重元素的全排列。

子树的位置确定好以后,然后再确定子树中各个节点的顺序。

对了,因为求组合数会用到除法,而且1e9+7又是个素数,所以我们在做除法的时候只要乘上它对应的乘法逆元即可。

这是递归计算的代码:

#include <bits/stdc++.h>

using namespace std;

const int maxn = 40000 + 10;
const int MOD = 1000000007;

int n, m;
vector<int> sons[maxn];
int fa[maxn], fac[maxn], ifac[maxn], inverse[maxn], sonsize[maxn];

inline int mul_mod(int a, int b, int n = MOD)
{
a %= n; b %= n;
return (int)((long long)a * b % n);
}

void gcd(int a, int b, int& d, int& x, int& y)
{
if(!b) { d = a; x = 1; y = 0; }
else{ gcd(b, a%b, d, y, x); y -= x*(a/b); }
}

int inv(int a, int n = MOD)
{
int d, x, y;
gcd(a, n, d, x, y);
return d == 1 ? (x+n)%n : -1;
}

int C(int n, int m)
{ return mul_mod(mul_mod(fac
, ifac[m]), ifac[n-m]); }

void init()
{
fac[0] = ifac[0] = 1;
for(int i = 1; i < maxn; i++)
{
fac[i] = mul_mod(fac[i - 1], i);
inverse[i] = inv(i);
}
}

void count(int u, int& size)
{//统计以u为根的子树节点个数
size = 1;
int d = sons[u].size();
for(int i = 0; i < d; i++)
{
int sz;
count(sons[u][i], sz);
size += sz;
}
sonsize[u] += size;
}

int main()
{
//freopen("in.txt", "r", stdin);

init();
int T;
scanf("%d", &T);
while(T--)
{
memset(fa, 0, sizeof(fa));
memset(sonsize, 0, sizeof(sonsize));
for(int i = 0; i <= n; i++) sons[i].clear();

scanf("%d%d", &n, &m);
for(int i = 0; i < m; i++)
{
int a, b;
scanf("%d%d", &a, &b);
fa[a] = b;
sons[b].push_back(a);
}
for(int i = 1; i <= n; i++)
if(!fa[i]) sons[0].push_back(i);
int size;
count(0, size);
//for(int i = 1; i <= n; i++) printf("%d\n", sonsize[i]);
int ans = fac
;
for(int i = 1; i <= n; i++) ans = mul_mod(ans, inverse[sonsize[i]]);
printf("%d\n", ans);
}

return 0;
}


代码君
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: