【51Nod1582】 n叉树

来源:互联网 发布:c语言是伪代码吗 编辑:程序博客网 时间:2024/06/15 01:24

有一棵n叉树,深度是无限的,每个结点有n个儿子。从左到右编号为1到n号儿子,第i号儿子离该结点的距离是di。现在要统计一下距离根结点不超过x的结点有多少个。
数字比较大对 109 + 7 取余后输出。
样例解释:
图中黄色的结点是距离根不超3的。
这里写图片描述

Input
单组测试数据。
第一行有两个整数n和x (1≤n≤10^5,0≤x≤10^9),表示每个结点的儿子数目,以及上文提到的x。
第二行有n个整数d1,d2,d3,…,dn (1≤di≤100)。
Output
输出结果占一行。
Input示例
样例输入1
3 3
1 2 3
Output示例
样例输出1
8

题解
记dp[i]表示距离为i的点的个数,dp[i]+=dp[i-d[j]],类似于斐波那契,可用矩阵乘法优化。

代码

#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#include<set>#include<ctime>#include<vector>#include<cmath>#include<algorithm>#include<map>#include<queue>#define mod 1000000007#define ll long long  using namespace std;inline int read(){    int x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}const int N=102;int ans[N+2][N+2],a[N+2][N+2],dp[N+2];int n,x,d[100005];void mul(int a[N+2][N+2],int b[N+2][N+2],int ans[N+2][N+2]){    int tmp[N+2][N+2];    for(int i=1;i<=N;i++)        for(int j=1;j<=N;j++)        {            tmp[i][j]=0;            for(int k=1;k<=N;k++)                tmp[i][j]=(tmp[i][j]+(ll)a[i][k]*b[k][j]%mod)%mod;        }    for(int i=1;i<=N;i++)        for(int j=1;j<=N;j++)            ans[i][j]=tmp[i][j];}int main(){    n=read();x=read();if (x==0) return printf("1"),0;    for (int i=1;i<=n;i++)    {        int D=read();        d[i]=D;        a[1][D]++;        a[N][D]++;    }     a[N][N]=1;    dp[0]=1;int sum=1;    for (int i=1;i<=100;i++)    {        a[i+1][i]=1;        for (int j=1;j<=n;j++)            if (d[j]<=i) dp[i]=(dp[i]+dp[i-d[j]])%mod;        sum=(sum+dp[i])%mod;        if (i==x) return printf("%d",sum),0;    }    x-=100;    for (int i=1;i<=N;i++) ans[i][i]=1;    while (x)    {        if (x&1) mul(ans,a,ans);        x>>=1;        mul(a,a,a);    }    int answer=0;dp[101]=sum;    for (int i=1;i<=N;i++)    {        int q=N-i-1;        if (q<0) q=101;        answer=(answer+(ll)ans[N][i]*dp[q]%mod)%mod;    }    printf("%d",answer);    return 0;}
原创粉丝点击