【题目】
k
k
k堆石子,每堆石子初始数量均为
n
n
n,编号
0
0
0~
k
−
1
k-1
k−1,第
i
i
i次操作对第
(
i
−
1
)
%
k
(i-1)\%k
(i−1)%k堆石子操作,可以从该堆石子中拿走若干石子(至少要拿走一颗),要求拿走后这堆石子的个数是拿走前这堆石子个数的一个约数。当某堆石子被取走若干石子后变成
1
1
1时结束操作。问最终操作结束于第
i
i
i堆的方案数 。答案对
985661441
985661441
985661441取模。
其中
n
=
∏
i
=
1
m
p
i
e
i
,
m
,
k
≤
10
,
2
≤
p
i
≤
1
0
9
,
∑
e
i
≤
1
0
5
,
1
≤
e
i
n=\prod^m_{i=1}p_i^{e_i},m,k\leq 10,2\leq p_i \leq 10^9,\sum e_i\leq 10^5,1\leq e_i
n=∏i=1mpiei,m,k≤10,2≤pi≤109,∑ei≤105,1≤ei
原题地址
【解题思路】
这个题很难啊qwq,反正我是没有想到的。那么下面引用官方题解
显然每个石子堆最多做
w
=
∑
i
=
1
m
e
i
w=\sum_{i=1}^m e_i
w=∑i=1mei次操作。
现在我们定义
f
(
x
)
f(x)
f(x)为某堆石子做
x
x
x次操作后恰好变为
1
1
1个的生成函数。那么我们可以得到每个数字做少于
x
x
x次操作不变为
1
1
1的生成函数也是
f
(
x
)
f(x)
f(x)。(这两个东西显然是一一对应的)
现在统计结束于第
i
i
i堆石子的情况,我们枚举它是第几次操作时结束的,不妨设为
x
x
x,那么对应的方案数就是
f
(
x
+
1
)
i
−
1
f
(
x
)
k
−
i
+
1
f(x+1)^{i-1}f(x)^{k-i+1}
f(x+1)i−1f(x)k−i+1,这是因为编号
≤
i
\leq i
≤i的石子堆在
x
x
x次操作后仍然没有变为
1
1
1,编号
≥
i
\geq i
≥i的石子堆在
x
−
1
x-1
x−1次操作后没有变为
1
1
1,只有编号为
i
i
i的石子堆恰好变为
1
1
1。
那么如果我们知道
f
(
x
)
f(x)
f(x),则可以用
O
(
w
k
)
O(wk)
O(wk)的时间解决问题。
考虑
f
(
x
)
f(x)
f(x)的组成,不妨设某种方案第
j
j
j次操作
e
i
e_i
ei减少了
d
(
i
,
j
)
d(i,j)
d(i,j),我们知道对于每个
e
i
e_i
ei有
∑
j
=
1
x
d
(
i
,
j
)
=
e
i
\sum_{j = 1}^{x}{d(i, j)} = e_i
∑j=1xd(i,j)=ei,且对于每个
j
j
j有
∑
i
=
1
m
d
(
i
,
j
)
>
0
\sum_{i = 1}^{m}{d(i, j)} \gt 0
∑i=1md(i,j)>0。
那么
f
(
x
)
f(x)
f(x)就是分配
d
(
i
,
j
)
(
1
≤
i
≤
m
,
1
≤
j
≤
x
)
d(i,j)(1 \leq i \leq m, 1 \leq j \leq x)
d(i,j)(1≤i≤m,1≤j≤x)使得它满足上面两个条件的方案数。
如果只考虑第一个条件对应的方案数
g
(
x
)
g(x)
g(x),我们可以规约到一个经典的组合问题,求一个方程非负整数解的个数。
g
(
x
)
=
∏
i
=
1
m
(
e
i
+
x
−
1
x
−
1
)
\displaystyle g(x) = \prod_{i = 1}^{m}{e_i + x - 1 \choose x - 1}
g(x)=i=1∏m(x−1ei+x−1)
这个公式可以通过对 x − 1 x-1 x−1个隔板, e i e_i ei个物品的排列数量得到,隔板把区间分成了 x x x块,每块区间中物品的数量对应一个变量,排列的数量等同于隔板选位置的方法数。
观察到如果某些
j
j
j与第二个条件产生了矛盾,与之相关的
d
(
i
,
j
)
d(i,j)
d(i,j)都会是零。利用容斥原理可以得到
f
(
x
)
=
∑
y
=
0
x
(
−
1
)
x
−
y
(
x
y
)
g
(
y
)
\displaystyle f(x) = \sum_{y = 0}^{x}{(-1)^{x - y} {x \choose y} g(y)}
f(x)=y=0∑x(−1)x−y(yx)g(y)
可以发现:
f
(
x
)
x
!
=
∑
y
=
0
x
(
−
1
)
x
−
y
(
x
−
y
)
!
⋅
g
(
y
)
y
!
\displaystyle \frac{f(x)}{x!} = \sum_{y = 0}^{x}{\frac{(-1)^{x - y}}{(x - y)!} \cdot \frac{g(y)}{y!}}
x!f(x)=y=0∑x(x−y)!(−1)x−y⋅y!g(y)
用
拆
系
数
F
F
T
拆系数FFT
拆系数FFT或
N
T
T
NTT
NTT即可。
(
985661441
=
235
×
2
22
+
1
)
(985661441 = 235 \times 2^{22} + 1)
(985661441=235×222+1)
总的时间复杂度是 O ( w log n + w k ) O(w \log n + w k) O(wlogn+wk)。
【参考代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2e5+10;
const int mod=985661441,g=985661438;
int n,m,k,L,pris;
LL fac[N],ifac[N],f[N],e[N],rev[N<<1],a[N<<1],b[N<<1];
LL qpow(LL x,LL y){LL ret=1;for(;y;y>>=1,x=x*x%mod) if(y&1) ret=ret*x%mod;return ret;}
LL C(int n,int m){return fac[n]*ifac[m]%mod*ifac[n-m]%mod;}
void up(LL &x,LL y){x+=y;if(x>=mod)x-=mod;}
void totinit()
{
fac[0]=fac[1]=1;
for(int i=2;i<N;++i) fac[i]=fac[i-1]*i%mod;
ifac[N-1]=qpow(fac[N-1],mod-2);
for(int i=N-1;i;--i) ifac[i-1]=ifac[i]*i%mod;
}
void ntt(LL *a,int n,int f)
{
for(int i=0;i<n;++i) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int i=1;i<n;i<<=1)
{
LL wn=qpow(g,f==1?(mod-1)/i/2:mod-1-(mod-1)/i/2);
for(int j=0;j<n;j+=(i<<1))
{
LL w=1;
for(int k=0;k<i;++k,w=w*wn%mod)
{
LL x=a[j+k],y=w*a[i+j+k]%mod;
a[j+k]=(x+y)%mod;a[i+j+k]=(x-y+mod)%mod;
}
}
}
LL inv=qpow(n,mod-2);
if(f==-1) for(int i=0;i<n;++i) a[i]=a[i]*inv%mod;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("HDU6036.in","r",stdin);
freopen("HDU6036.out","w",stdout);
#endif
totinit();int cas=0;
while(scanf("%d%d",&pris,&k)!=EOF)
{
++cas;printf("Case #%d: ",cas);n=0;
for(int i=1,x;i<=pris;++i) scanf("%d%d",&x,&e[i]),n+=e[i];
for(L=0,m=1;m<=n<<1;m<<=1,++L);
for(int i=0;i<m;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1)),a[i]=b[i]=0;
for(int i=0;i<=n;++i) a[i]=(i&1)?mod-ifac[i]:ifac[i];
for(int i=1;i<=n;++i)
{
b[i]=ifac[i];
for(int j=1;j<=pris;++j) b[i]=b[i]*C(e[j]+i-1,i-1)%mod;
}
ntt(a,m,1);ntt(b,m,1);
for(int i=0;i<m;++i) a[i]=a[i]*b[i]%mod;
ntt(a,m,-1);
for(int i=0;i<=n;++i) f[i]=a[i]*fac[i]%mod; f[n+1]=0;
for(int i=1;i<=k;++i)
{
LL ans=0;
for(int j=1;j<=n;++j) up(ans,qpow(f[j],k-i+1)*qpow(f[j+1],i-1)%mod);
if(i<k) printf("%lld ",ans); else printf("%lld\n",ans);
}
}
return 0;
}
【总结】
论如何构造生成函数计算。