【51Nod1582】 n叉树

有一棵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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值