题面:
题意:
有棵无限大的有根树,每个节点都有N个孩子,每个孩子距离父亲节点的距离为di.求距离根节点距离<=x的节点个数。
题解:
注意到 di <= 100
我们 设 dp [ i ] 为距离根节点为 i 的点的个数,sum [ i ] 为距离根节点小于等于 i 的点的个数。
cnt [ i ] 为距离父亲节点 为 i 的点的个数。
那么有转移方程。
for(int i=1;i<=100;i++)
{
for(int j=1;j<=i;j++)
{
dp[i]=(dp[i]+dp[i-j]*cnt[j])%MOD;
}
sum[i]=(sum[i-1]+dp[i])%MOD;
}
注意到 1<=di<=100。
那么 dp [ x ] 只能由 dp[x - 1 ]----dp [ x - 100 ] 转移而来。
用矩阵快速幂优化。只保存一百项 dp,在转移的过程中同时计算 sum
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<bitset>
#include<map>
#include<set>
#define ll long long
#define llu unsigned ll
#define pr make_pair
#define pb push_back
#define ui unsigned int
#define lc (cnt<<1)
#define rc (cnt<<1|1)
#define len(x) (t[(x)].r-t[(x)].l+1)
#define tmid ((l+r)>>1)
#define forhead(x) for(int i=head[(x)];i;i=nt[i])
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
using namespace std;
const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e18;
const int mod=1e9+7;
const double eps=1e-8;
const double pi=acos(-1.0);
const int maxn=100100;
const int maxm=100100;
const int up=100000;
const int hashp=13331;
const int hashpp=131;
struct node
{
ll a[110][110];
};
node operator * (const node &a,const node &b)
{
node c;
memset(c.a,0,sizeof(c.a));
for(int i=1;i<=101;i++)
{
for(int j=1;j<=101;j++)
{
for(int k=1;k<=101;k++)
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%mod;
}
}
return c;
}
node mypow(node a,ll b)
{
node ans;
memset(ans.a,0,sizeof(ans.a));
for(int i=1;i<=101;i++)
ans.a[i][i]=1;
while(b)
{
if(b&1) ans=ans*a;
a=a*a;
b>>=1;
}
return ans;
}
ll d[maxn];
ll cnt[110],dp[110],sum[110];
int main(void)
{
int n,x;
scanf("%d%d",&n,&x);
for(int i=1;i<=n;i++)
{
scanf("%lld",&d[i]);
cnt[d[i]]++;
}
sum[0]=dp[0]=1;
for(int i=1;i<=100;i++)
{
for(int j=1;j<=i;j++)
dp[i]=(dp[i]+dp[i-j]*cnt[j])%mod;
sum[i]=(sum[i-1]+dp[i])%mod;
}
if(x<=100)
{
printf("%lld\n",sum[x]);
return 0;
}
node ans;
memset(ans.a,0,sizeof(ans.a));
for(int i=1;i<=99;i++)
ans.a[i][i+1]=1;
for(int i=1;i<=100;i++)
ans.a[101][i]=ans.a[100][i]=cnt[100-i+1];
ans.a[101][101]=1;
ans=mypow(ans,x-100);
ll res=0;
for(int i=1;i<=100;i++)
res=(res+dp[i]*ans.a[101][i])%mod;
res=(res+sum[100]*ans.a[101][101])%mod;
printf("%lld\n",res);
return 0;
}
于是,我又想到,这个dp和sum都是线性的,那么直接杜教BM可否?
于是直接打到 dp [ 1000 ] , sum [ 1000 ] 然后杜教。
比我矩阵快速幂快多了。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<bitset>
#include<map>
#include<set>
#define ll long long
#define llu unsigned ll
#define pr make_pair
#define pb push_back
#define ui unsigned int
#define lc (cnt<<1)
#define rc (cnt<<1|1)
#define len(x) (t[(x)].r-t[(x)].l+1)
#define tmid ((l+r)>>1)
#define forhead(x) for(int i=head[(x)];i;i=nt[i])
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
using namespace std;
const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e18;
const int mod=1e9+7;
const double eps=1e-8;
const double pi=acos(-1.0);
const int maxn=100100;
const int maxm=100100;
const int up=100000;
const int hashp=13331;
namespace linear_seq
{
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef pair<int,int> PII;
const int N=10010;
ll res[N],base[N],c[N],md[N];
vector<int> Md;
const ll mod=1e9+7;
ll powmod(ll a,ll b)
{
ll res=1;
a%=mod;
for(;b;b>>=1)
{
if(b&1)
res=res*a%mod;
a=a*a%mod;
}
return res;
}
void mul(ll *a,ll *b,int k)
{
for(int i=0;i<k+k;++i) c[i]=0;
for(int i=0;i<k;++i)
if(a[i])for(int j=0;j<k ;++j)
c[i+j]=(c[i+j]+a[i]*b[j])%mod;
for (int i=k+k-1;i>=k;i--)
if(c[i])for(int j=0;j<(int)Md.size();++ j)
c[i-k+Md[j]]=(c[i-k+Md[j]]-c[i]*md[Md[j]])%mod;
for(int i=0;i<k;++i) a[i]=c[i];
}
int solve(ll n,VI a,VI b)
{
ll ans=0,pnt=0;
int k=SZ(a);
for(int i=0;i<k;++i) md[k-1-i]=-a[i];
md[k]=1;Md.clear();
for(int i=0;i<k;++i)
if(md[i]!=0)Md.push_back(i);
for(int i=0;i<k;++i) res[i]=base[i]=0;
res[0]=1;
while ((1ll<<pnt)<=n)
pnt++;
for (int p=pnt;p>=0;p--)
{
mul(res,res,k);
if ((n>>p)&1)
{
for (int i=k-1;i>=0;i--) res[i+1]=res[i];
res[0]=0;
for(int j=0;j<(int)Md.size();++ j)
res[Md[j]]=(res[Md[j]]-res[k]*md[Md[j]])%mod;
}
}
for(int i=0;i<k;i++) ans=(ans+res[i]*b[i])%mod;
if (ans<0) ans+=mod;
return ans;
}
VI BM(VI s)
{
VI C(1,1),B(1,1);
int L=0,m=1,b=1;
for(int n=0;n<(int)s.size();++n)
{
ll d=0;
for(int i=0;i<L+1;++ i)
d=(d+(ll)C[i]*s[n-i])%mod;
if(d==0) ++m;
else if(2*L<=n)
{
VI T=C;
ll c=mod-d*powmod(b,mod-2)%mod;
while (SZ(C)<SZ(B)+m)C.push_back(0);
for(int i=0;i<(int)B.size();++i) C[i+m]=(C[i+m]+c*B[i])%mod;
L=n+1-L; B=T; b=d; m=1;
}
else
{
ll c=mod-d*powmod(b,mod-2)%mod;
while (SZ(C)<SZ(B)+m)C.push_back(0);
for(int i=0;i<(int)B.size();++ i) C[i+m]=(C[i+m]+c*B[i])%mod;
++m;
}
}
return C;
}
ll gao(VI a,ll n)
{
VI c=BM(a);
c.erase(c.begin());
for(int i=0;i<(int)c.size();++i) c[i]=(mod-c[i])%mod;
return (ll)solve(n,c,VI(a.begin(),a.begin()+SZ(c)));
}
};
ll d[maxn];
ll cnt[1100],dp[1100],sum[1100];
vector<int>vc;
int main(void)
{
int n,x;
scanf("%d%d",&n,&x);
for(int i=1;i<=n;i++)
{
scanf("%lld",&d[i]);
cnt[d[i]]++;
}
sum[0]=dp[0]=1;
vc.pb(1);
for(int i=1;i<=1000;i++)
{
for(int j=1;j<=min(i,100);j++)
dp[i]=(dp[i]+dp[i-j]*cnt[j])%mod;
sum[i]=(sum[i-1]+dp[i])%mod;
vc.pb(sum[i]);
}
printf("%lld\n",linear_seq::gao(vc,x));
return 0 ;
}