神仙题
题目大意:
定义
为可重集合S的个数,其中S满足S的元素都是m的非负整数次幂,并且S的元素和是n。
定义
,求:
。
题解:
考虑
其实是,你有
,用这些数字拼出
的方案数,并且这些数字是无序的(即
要出现在
之后,
在
之后)。
如果要求 ,那么其实是,你先将 表示成 ,然后每个 用 表示出来,加起来就是,要用: (总共k段)拼出n,然后拼的时候元素要是无序的(即尽管有些数字的值相同,但是标号是不同的,视为不同的数字,然后要满足标号不减)。
假定现在要求
(一项)。
显然数字顺序不重要,可以重新写成
,每段有k个。
那么考虑一个普及组级别的dp,设
表示考虑了前i个数字,和为j的方案数。显然有(记第
个数字是
)枚举当前数字用了几个:
。
然后可以注意到,假设我已经算出了
,那么因为我是重新排列过数字的顺序了,所以之后加入的数字都是
的倍数,那么第二维模
的余数就不会变了。最终只关心n,所以
只关心
的那些
。
也就是说,若
不满足上述条件可以从现在开始不管了。
那么既然余数只关心
,相当于是个定值,因此现在只需要表示前面的倍数即可,即重新令:
。
其转移可以直接写出:
注意由于
是重新排列过的,因此后面的a是前面的倍数,那么
要么是1,要么是m,总之是个只和i有关的常数,后面的那个也是。
也就是
。
有个结论是,上面那个东西,如果
是个关于第二维的
次多项式,那么
是关于第二维的
次多项式。
(其实你取
,就是经典的“多项式的前缀和还是多项式并且次数加一”)
然后来填一开始的
的前缀和的坑。
如果从一开始就理解为,求前缀和是用那些数字拼出一个大小不超过n 的数字s,然后新增
这个数字,其方案数是1,或者说
只能使用
(而不是
)来拼成,那么就是用
个
和
个
拼出n的方案数。
这样每次转移以及最后求答案的时候暴力拉格朗日插值即可。
注意最后要求的是
实现的时候要注意小心爆int/longlong之类的。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define mod 1000000007
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
const int N=100010,T=3000;
inline int P(int &x) { return x>=mod?x-=mod:x; }
int fac[T],facinv[T];
inline int fast_pow(int x,int k,int ans=1) { for(;k;k>>=1,x=(lint)x*x%mod) (k&1)?ans=(lint)ans*x%mod:0;return ans; }
inline int sol(int x,int s) { return (s&1)?(x?mod-x:0):x; }
inline int prelude(int n)
{
fac[0]=1;rep(i,1,n) fac[i]=(lint)fac[i-1]*i%mod;
facinv[n]=fast_pow(fac[n],mod-2);
for(int i=n-1;i>=0;i--) facinv[i]=(i+1ll)*facinv[i+1]%mod;
return 0;
}
struct poly{
vector<int> f,pre,suf,xs;int n;
inline int resize(int _n)
{
return n=_n+1,f.resize(n),pre.resize(n),suf.resize(n),xs.resize(n),0;
}
inline int PRE(int x) { return x<0?1:pre[x]; }
inline int SUF(int x) { return x>=n?1:suf[x]; }
inline int getxs()
{
rep(i,0,n-1) xs[i]=(lint)f[i]*facinv[i]%mod*facinv[n-1-i]%mod;
return 0;
}
inline int F(lint x)
{
int ans=0;if(x<n) return f[(int)x];x%=mod;
rep(i,0,n-1) pre[i]=PRE(i-1)*(x-i)%mod;
for(int i=n-1;i>=0;i--) suf[i]=SUF(i+1)*(x-i)%mod;
rep(i,0,n-1) ans+=sol((lint)xs[i]*PRE(i-1)%mod*SUF(i+1)%mod,n-i-1),P(ans);
return ans;
}
}h[T];lint mi[100],a[T];
int main()
{
lint n,m;int k,cnt=0,t=0;mi[0]=1;cin>>n>>m>>k;
while(mi[cnt]<=n/m) mi[cnt+1]=mi[cnt]*m,cnt++;
rep(i,0,cnt) rep(j,1,k) a[++t]=mi[i];
a[0]=1,prelude(t);
h[0].resize(0),h[0].f[0]=1,h[0].getxs();
rep(i,1,t)
{
h[i].resize(i);lint r=n%a[i]/a[i-1];
rep(j,0,i)
h[i].f[j]=(j?h[i].f[j-1]:0)+h[i-1].F(a[i]/a[i-1]*j+r),P(h[i].f[j]);
h[i].getxs();
}
return !printf("%d\n",h[t].F(n/a[t]));
}