测试地址:Shell Necklace
题目大意:一串链形贝壳项链(不是环形),有 ai a i 种方案装饰连续 i i 个贝壳,问装饰整串项链有多少种方案。
做法:本题需要用到CDQ分治+FFT(分治FFT)。
首先令 f(i) f ( i ) 为装饰长为 i i 的项链的方案数,特殊地,令 f(0)=1 f ( 0 ) = 1 ,那么我们很快能得出状态转移方程:
f(i)=∑ij=1f(i−j)aj f ( i ) = ∑ j = 1 i f ( i − j ) a j
直接计算这个方程是 O(n2) O ( n 2 ) 的,不能接受。注意到等号右边的部分是一个卷积形式的式子,但是这个卷积和往常普通FFT可以做的卷积不同,它涉及到 f f 自己和另一个数组的卷积,所以我们不能用普通的FFT来解决问题。
这时候分治FFT就出场了。分治FFT也可以写成CDQ分治+FFT。我们知道CDQ分治的主要思想就是对于每个区间 [l,r] [ l , r ] ,先递归计算区间 [l,mid] [ l , m i d ] 中的答案,然后计算 [l,mid] [ l , m i d ] 对 [mid+1,r] [ m i d + 1 , r ] 中答案的贡献,最后递归计算区间 [mid+1,r] [ m i d + 1 , r ] 。这里我们采用类似的过程,先算出区间 [l,mid] [ l , m i d ] 中所有的 f f ,然后将这一段 f f 和 a a 做卷积,求出它们对区间 [mid+1,r] [ m i d + 1 , r ] 中所有 f f 的贡献,用FFT即可,最后计算区间 [mid+1,r] [ m i d + 1 , r ] 。根据主定理,这个算法的时间复杂度是 O(nlog2n) O ( n log 2 n ) 的,可以通过此题。
以下是本人代码:
#include
using namespace std;
typedef long long ll;
const ll mod=313;
const double pi=acos(-1.0);
int n,rev[400010];
ll A[100010],f[100010];
struct Complex
{
double x,y;
}a[400010],b[400010];
Complex operator + (Complex a,Complex b) {Complex s={a.x+b.x,a.y+b.y};return s;}
Complex operator - (Complex a,Complex b) {Complex s={a.x-b.x,a.y-b.y};return s;}
Complex operator * (Complex a,Complex b) {Complex s={a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x};return s;}
void FFT(Complex *a,int type,int n)
{
for(int i=0;iif (ifor(int mid=1;mid1)
{
Complex W={cos(pi/mid),type*sin(pi/mid)};
for(int l=0;l1))
{
Complex w={1.0,0.0};
for(int k=0;kif (type==-1)
{
for(int i=0;idouble)n;
}
}
void solve(int l,int r)
{
if (l==r) return;
int mid=(l+r)>>1;
solve(l,mid);
int bit=0,x=1;
while(x<((r-l+1)<<1)) x<<=1,bit++;
for(int i=0;i0.0;
for(int i=0;i1;i++)
a[i].x=f[l+i];
for(int i=0;i1];
rev[0]=0;
for(int i=1;i>1]>>1)|((i&1)<<(bit-1));
FFT(a,1,x),FFT(b,1,x);
for(int i=0;i1,x);
for(int i=mid+1;i<=r;i++)
{
f[i]+=a[i-l-1].x+0.5;
f[i]%=mod;
}
solve(mid+1,r);
}
int main()
{
while(scanf("%d",&n)&&n)
{
memset(f,0,sizeof(f));
f[0]=1;A[0]=0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&A[i]);
A[i]%=mod;
}
solve(0,n);
printf("%lld\n",f[n]);
}
return 0;
}