亲戚(relative)

【题目背景】

Y 家是世界上最大的家族,HJZ 是其中一员。

现在 Y 家人想要拍一张全家福,却发现这是一个十分复杂的问题. . . . . .

【题目描述】

Y 家一共有 n

其中每个人最多有一个直系祖先。

Y 家的家规十分严格,在拍全家福时每个人必须排在其直系祖先后面。如 HZY 不

可以排在 HJZ 前面,但 Little_Meat_Circle 就可以排在 HJZ 前。

现在 HJZ 想知道可以有多少种合法的排队方案。你只需要给出方案数对 109 + 7 取模的结果。

【输入格式】

从文件 relative.in 中读入数据。

第一行一个正整数 n 表示 Y 家的人数。

第二行 n 个整数 fi 表示第 i 个人的直系祖先编号。若 fi = 0 则表示第 i 个人没有 直系祖先。保证 fi , i

【输出格式】

输出到文件 relative.out 中。

一行一个整数,表示合法的方案数对 109 + 7 取模的结果。

【样例 1 输入】

4

0 1 1 0

【样例 1 输出】

8

题解:

首先,一个节点在以它为根的子树的方案中必为第一位

所以以它为根的子树的方案等于:

将所有子节点对应的子树的序列混合起来的方案数

因为序列顺序不能改变,所以可以变成组合问题

假设一个子树对应的序列a长为x,一个子树对应的序列b长为y,组成一个x+y的序列

所以方案数为:从x+y个位置中选x个(或y个)=>C(x+y,x)

因为这只是一对序列的方案,所以要乘f[a]*f[b]

这里f[x]是指x点的子树的方案数

在多叉树中,分叉可能大于2,这没有关系

先算出两个儿子的值,合并起来,记下合并后的方案数和大小,作为一个节点与下一个儿子合并

sum=(sum*f[v])*(C(tmp,p))    (p是子树大小,tmp是当前大小+p,sum是当前方案数)

最后f[x]=sum;

 

下一个问题,这个过程已经是O(n)了,求组合数如果O(n)会超时

方法是:

C(n,r)=n!/((n-r)!*r!)

n!用O(n)预处理,分母就用递推式

A[i]=(Mod-Mod/i)*A[Mod%i]%Mod

在O(n)时间内求出阶乘逆元

组合数就可以O(1)搞定了

总复杂度为O(n)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 struct Node
 8 {
 9     int next,to;
10 }edge[400001];
11 int head[200001],Mod=1000000007,n,num;
12 long long f[200001],A[200001],B[200001];
13 void add(int u,int v)
14 {
15     num++;
16     edge[num].next=head[u];
17     head[u]=num;
18     edge[num].to=v;
19 }
20 long long C(int x,int r)
21 {
22  if (x==r) return 1;
23  if (r==0) return 1;
24     long long s=B[x];
25     s=(s*A[x-r])%Mod;
26     s=(s*A[r])%Mod;
27     //cout<<x<<' '<<r<<' '<<s<<endl;
28     return s;
29 }
30 int dfs(int x,int pa)
31 {int i,p;
32     int tmp=0;
33     long long sum=1;
34     for (i=head[x];i;i=edge[i].next)
35     {
36       int v=edge[i].to;
37        if (v!=pa)
38        {
39          p=dfs(v,x);
40          //cout<<v<<' '<<f[v]<<' '<<p<<endl;
41           tmp+=p;
42           sum=(((sum*f[v])%Mod)*C(tmp,p))%Mod;
43        }
44     }
45   f[x]=sum;
46   //cout<<sum<<' '<<x<<endl;
47   return tmp+1;
48 }
49 int main()
50 {int i,x,j;
51 //freopen("relative.in","r",stdin);
52 //freopen("relative.out","w",stdout);
53     cin>>n;
54     for (i=1;i<=n;i++)
55     {
56         scanf("%d",&x);
57         add(x,i);
58     }
59     B[0]=1;
60     for (i=1;i<=n;i++)
61     B[i]=(B[i-1]*i)%Mod;
62      A[1]=1;
63      for (i=2;i<=n;i++)
64      A[i]=((Mod-(Mod/i))*(long long)A[Mod%i])%Mod;
65      for (i=2;i<=n;i++)
66      A[i]=(A[i]*A[i-1])%Mod;
67     dfs(0,0);
68 cout<<f[0];
69 }

 

posted @ 2017-08-17 17:19  Z-Y-Y-S  阅读(904)  评论(0编辑  收藏  举报