题意:给你一颗树,有无限个节点,每个节点有n个儿子,据他第i个儿子的距离为di,问这棵树有多少个点离根的距
离不超过x。
思路:
f[i]表示路径长度恰为i的能到的点个数,首先可以列出一个DP:
f[i]=∑nj=1f[i−dj]
,边界是
f[0]=1
。
似乎不是那么好做。但是注意到,
di<=100
,这意味着DP可以简化一下,变成连续的;
f[i]=∑100j=1f[i−j]∗cnt[j]
,其中cnt[j]表示有多少条边权为j的边。要求的答案是
∑xi=0f[i]
,cnt可以预处理出来,接下来就是很经典的矩阵快速幂啦! (借鉴:点击打开)
构造矩阵:
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 105;
const int mod = 1e9+7;
ll n, x, cnt[maxn];
ll dp[maxn];
struct node
{
ll s[maxn][maxn];
};
node mul(node a, node b)
{
node t;
memset(t.s, 0, sizeof(t.s));
for(int i = 1; i <= 101; i++)
for(int j = 1; j <= 101; j++)
for(int k = 1; k <= 101; k++)
t.s[i][j] = (t.s[i][j]+a.s[i][k]*b.s[k][j])%mod;
return t;
}
node mt_pow(node a, int q)
{
node res;
memset(res.s, 0, sizeof(res.s));
for(int i = 1; i <= 101; i++)
res.s[i][i] = 1;
while(q)
{
if(q%2) res = mul(res, a);
a = mul(a, a);
q /= 2;
}
return res;
}
int main(void)
{
while(cin >> n >> x)
{
memset(cnt, 0, sizeof(cnt));
memset(dp, 0, sizeof(dp));
for(int i = 1; i <= n; i++)
{
int x;
scanf("%d", &x);
cnt[x]++;
}
dp[0] = 1;
for(int i = 1; i < maxn; i++)
for(int j = 1; j <= i; j++)
dp[i] = (dp[i]+cnt[j]*dp[i-j]%mod)%mod;
ll sum = 0;
if(x <= 100)
{
for(int i = 0; i <= x; i++)
sum = (sum+dp[i])%mod;
printf("%I64d\n", sum);
continue;
}
for(int i = 0; i <= 100; i++)
sum = (sum+dp[i])%mod;
node base;
memset(base.s, 0, sizeof(base.s));
for(int i = 1; i <= 99; i++)
base.s[i][i+1] = 1;
for(int i = 1; i <= 100; i++)
base.s[100][i] = base.s[101][i] = cnt[101-i];
base.s[101][101] = 1;
node ans = mt_pow(base, x-100);
ll res = 0;
for(int i = 1; i <= 100; i++)
res = (res+ans.s[101][i]*dp[i]%mod)%mod;
res = (res+ans.s[101][101]*sum%mod)%mod;
printf("%I64d\n", res);
}
return 0;
}