CF717A Festival Organization 题解
首先考虑求出长度为 \(i\) 的合法串的个数。
很明显可以想到用 dp 解。
设 \(f_{i,0/1}\) 为长度为 \(i\) 最后一位为 \(0/1\) 的合法串个数。
可以很容易想到转移方程:
\(f_{i,0}=f_{i-1,0}\)
\(f_{i,1}=f_{i-1,1}+f_{i-1,0}\)
第一个方程代入第二个方程:
\(f_{i,1}=f_{i-1,1}+f_{i-2,1}\)
答案为 \(f_{i,0}+f{i,1}\)。
即为 \(f_{i-1,1}+f_{i,1}=f_{i+1,1}\)
那么可以省掉一位,转移方程重新改为:
\(f_i=f_{i-1}+f_{i-2}\)
这个也同样是斐波那契的通项式,由于 \(f_1=2\),所以是斐波那契整体往左移动了两位。
我们知道斐波那契数列通项式为:
\(F_i=\frac{1}{\sqrt{5}}(\frac{1+\sqrt{5}}{2})^i-\frac{1}{\sqrt{5}}(\frac{1-\sqrt{5}}{2})^i\)
这样明显是会 T 的,而且还要求 \(\sum_{i=l}^r\binom{f_i}{k}\),所以没法直接用斐波那契通项式求解。
首先拆开 \(\binom{f_i}{k}\)。
\(\binom{f_i}{k}=\frac{f_i^{\underline{k}}}{k!}\)。
\(f_i^{\underline{k}}\) 为下降次幂,代表 \(f_i\times (f_i-1)\times ...\times(f_i-k+1)\)。
下降次幂可以用第一类斯特林数拆开,为:
\(x^{\underline{k}}=\sum_{i=1}^k(-1)^{k-i}\times \left[^k_i\right]\times x^i\)。
\(\left[^x_y\right]\) 代表第一类斯特林数,将 \(x\) 个不同的数划分成 \(y\) 个相同的圆排列。可以递推求解,接下来用 \(s_{x,y}\) 代替第一类斯特林数:
\(s_{x,y}=s_{x-1,y-1}+s_{x-1,y}\times(x-1)\)。
左边的组合意义为 \(x\) 个数有序的选了 \(k\) 个的方案数。
右边即是将 \(k\) 个数划分成 \(i\) 个圆排列,再将有标号的圆排列放入 \(x\) 个有标号的盒子中,允许空,再做容斥。那么最后右边会得到 \(k\) 个大小为 \(1\) 的有标号的圆排列放入 \(x\) 个盒子中。
很明显左右等价,等式成立。
那么原式可以正常转化为:
\(\sum_{i=l}^r\frac{\sum_{j=0}^k(-1)^{k-j}\times s_{k,j}\times f_i^j}{k!}\)
提出 \(\frac{1}{k!}\),原式为:
\(\frac{1}{k!}\sum_{i=l}^r\sum_{j=0}^k(-1)^{k-j}\times s_{k,j}\times f_i^j\)。
设 \(A=\frac{1}{\sqrt{5}},B=-A,C=\frac{1+\sqrt{5}}{2},D=\frac{1-\sqrt{5}}{2}\)。
代入得:
\(\frac{1}{k!}\sum_{i=l}^r\sum_{j=0}^k(-1)^{k-j}\times s_{k,j}\times (AC^i+BD^i)^j\)。
牛顿二项式定理拆开最后一项:
\(\frac{1}{k!}\sum_{i=l}^r\sum_{j=0}^k(-1)^{k-j}\times s_{k,j}\times \sum_{p=0}^j(AC^i)^p(BD^i)^{p-j}\)。
\(A,B\) 与 \(i\) 无关,提出。
\(\frac{1}{k!}\sum_{j=0}^k(-1)^{k-j}\times s_{k,j}\times \sum_{p=0}^jA^pB^{j-p}\sum_{i=l}^r(C^pD^{p-j})^i\)。
前面 \(k^2\) 枚举就可以出来了,后面 \(C^pD^{p-j}\) 也只有 \(k^2\) 种可能。
那现在等价于就是求 \(g(x)=\sum_{n=0}^{r}x^n\),再用前缀和相减。
这玩意就是等比数列。正常求就完事了。
注意 \(\sqrt{5}\) 在取模 \(10^9+7\) 时没有二次剩余,所以可以把所有数写成 \(a+b\sqrt{5}\) 的形式,然后整体计算。
加与减就是整数相加与无理数系数相加,相减同理。
相乘时就拆括号就行了,相除时是分母有理化的过程。
还有注意,\(\frac{1}{\sqrt{5}}=\frac{\sqrt{5}}{5}\),所以系数的即为 \(5\) 的逆元。其他细节具体看代码吧。
时间复杂度:\(O(k^2)\)。
#include<iostream>
#include<cstdio>
#define int long long
using namespace std;
const int mod=1e9+7;
const int N=205;
int k,l,r,s[N][N],jie[N],ni[N];
struct node
{
int x,y;
node(int xx=0,int yy=0)
{
x=xx%mod,y=yy%mod;
}
}A[N],B[N],p1[N],p2[N];
int quick_pow(int x,int y)
{
int sum=1,num=x;
while(y)
{
if(y&1)sum*=num,sum%=mod;
num*=num;
num%=mod;
y>>=1;
}
return sum;
}
const int M=quick_pow(2,mod-2);
const node X(M,M);
const node Y(M,mod-M);
inline int C(int x,int y)
{
return jie[x]*ni[y]%mod*ni[x-y]%mod;
}
inline int add(int x,int y)
{
x+=y;
x=(x>=mod)?x-mod:x;
x=(x<0)?x+mod:x;
return x;
}
node operator +(node fi,node se)
{
return node(add(fi.x,se.x),add(fi.y,se.y));
}
node operator -(node fi,node se)
{
return node(add(fi.x,-se.x),add(fi.y,-se.y));
}
node operator *(node fi,node se)
{
return node((fi.x*se.x+fi.y*se.y*5)%mod,(fi.x*se.y+fi.y*se.x)%mod);
}
node operator /(node x,node y)
{
node z(y.x,mod-y.y);
x=x*z;
int num=y.x*y.x-y.y*y.y*5;
num=(num%mod+mod)%mod;
x=x*node(quick_pow(num,mod-2),0);
return x;
}
node quick_pow2(node x,int y)
{
node num=x,sum(1,0);
while(y)
{
if(y&1)sum=sum*num;
num=num*num;
y>>=1;
}
return sum;
}
node getnum(node x,int y)
{
if(x.x==1&&x.y==0)return y;
return (quick_pow2(x,y+1)-node(1,0))/(x-node(1,0));
}
node calc(int x,int y)
{
node num=p1[y]*p2[x-y];
return getnum(num,r)-getnum(num,l-1);
}
signed main()
{
scanf("%lld%lld%lld",&k,&l,&r);
l+=2,r+=2;
node res(1,0);
for(int i=1;i<=k;i++)res=res*node(i,0);
res=quick_pow2(res,mod-2);
s[0][0]=1;
for(int j=1;j<=k;j++)for(int i=1;i<=k;i++)s[i][j]=s[i-1][j-1]+s[i-1][j]*(i-1),s[i][j]%=mod;
A[0]={1,0};
B[0]={1,0};
node num=quick_pow2(node(0,mod-1),mod-2);//num(0,quick_pow(5,mod-2));
//node num2(0,mod-quick_pow(5,mod-2));
for(int i=1;i<=k;i++)
{
A[i]=A[i-1]*num;
B[i]=B[i-1]*num*node(mod-1,0);
}
jie[0]=ni[0]=1;
for(int i=1;i<=k;i++)jie[i]=jie[i-1]*i%mod,ni[i]=quick_pow(jie[i],mod-2);
p1[0]=p2[0]={1,0};
for(int i=1;i<=k;i++)p1[i]=p1[i-1]*X,p2[i]=p2[i-1]*Y;
node ans(0,0);
for(int i=0;i<=k;i++)
{
int ff=1;
if((k-i)&1)ff=-1;
node sum={0,0};
for(int j=0;j<=i;j++)sum=sum+node(C(i,j),0)*A[j]*B[i-j]*calc(i,j);
sum=sum*node(s[k][i]*ff,0);
//cout<<sum.x<<" "<<sum.y<<endl;
ans=ans+sum;
}
ans=ans*res;
printf("%lld",ans.x);
return 0;
}

浙公网安备 33010602011771号