luogu P5320 [BJOI2019]勘破神机

时间:2024-09-27 14:35:44

upd:20.04.27

orz EI

考虑把组合数拆成下降幂除掉阶乘

\(\sum_{i=0}^{n}\binom{f_i}{k}=\sum_{i=0}^{n}\frac{{f_i}^{\underline{k}}}{k!}=\frac{1}{k!}\sum_{i=0}^{n}{f_i}^{\underline{k}}\)

考虑算\(\sum_{i=0}^{n}{f_i}^{\underline{k}}\),带入通项公式\(f_i=\lambda_1\alpha^i+\lambda_2\beta^i\)

\(\sum_{i=0}^{n}{f_i}^{\underline{k}}=\sum_{i=0}^{n}(\lambda_1\alpha^i+\lambda_2\beta^i)^{\underline{k}}\)

\(=\sum_{i=0}^{n}\prod_{j=0}^{k-1}(\lambda_1\alpha^i+\lambda_2\beta^i-j)\)

注意到本题中在\(m=2\)时\(\alpha\beta=-1\),在\(m=3\)时\(\alpha\beta=1\)

所以先考虑\(m=3\),可得\(\beta=\alpha^{-1}\),代入得

\(=\sum_{i=0}^{n}\prod_{j=0}^{k-1}(\lambda_1\alpha^i+\lambda_2\alpha^{-i}-j)\)

设后面柿子展开后\((\alpha^i)^j\)前面系数为\(c_j\)

\(=\sum_{i=0}^{n}\sum_{j=-k}^{k}c_j(\alpha^i)^j\)

\(=\sum_{j=-k}^{k}c_j(\sum_{i=0}^{n}\alpha^{ij})\)

所以用分治fft算出\(c_j\)后就可以等比数列求和做了

还有\(m=2\)的情况,这时\(\alpha\beta=-1,\beta=-\alpha^{-1}\),代入后发现当\(i\)为奇数时\(\prod\)里面\(\lambda_2\)前面符号为负号,否则为正号,这时候分别算奇数偶数的答案即可.总复杂度\(O(k\log^2k+k\log n)\)

code


以下是原内容

传送门

首先我们要知道要求什么.显然每次放方块要放一大段不能从中间分开的部分.设\(m=2\)方案为\(f\),\(m=3\)方案为\(g\),\(m=2\)可以放一个竖的,或者两个横的,所以\(f_i=f_{i-1}+f_{i-2}\);\(m=3\),因为只有\(i\)为偶数有值,所以为了方便后面的\(i\)其实是原来的\(2i\),然后可以发现要么放三个横的,要么一横两竖,要么像下面这样放长度为偶数的块

|--|   ------
|--| |----|
---- |----|

注意到(原来的)长度为\(2\)有三种方案,其他的都有2种方案,所以\(g_i=3g_{i-1}+\sum_{j=2}^{i}2g_{i-j}\),打表可以发现其实是\(g_i=4g_{i-1}-g_{i-2}(g_1=3)\).因为要从某种长度中选\(k\)个,所以要求\((m=2)\sum_{i=l}^r \binom{f_i}{k}\).显然只要考虑\([1,r]\)和\([1,l-1]\)的值

\(k\)较小,我们可以把组合数看成一个\(k+1\)次项的多项式\(\binom{n}{k}=\frac{1}{k!}\prod_{i=0}^{k}(n-i)\),后面记第\(i\)项系数为\(s_i\),那么我们要求的是

\(\sum_{i=1}^n \sum_{j=0}^{k} s_j{f_i}^j\)

\(\sum_{j=0}^{k} s_j\sum_{i=1}^n{f_i}^j\)

后面那个东西不大好求,考虑我们已经知道递推式了,可以利用特征方程解出通项公式,可以去这里学,所以\(f\)的通项公式是自己去网上找,然后稍微改一下,\(g\)的公式是\(g_n=\frac{3-\sqrt{3}}{6}(2+\sqrt{3})^n+\frac{3+\sqrt{3}}{6}(2-\sqrt{3})^n\),后面为了方便,统一记为\(ab^n+cd^n\)

然后继续推式子

\(\sum_{j=0}^{k} s_j\sum_{i=1}^n(ab^i+cd^i)^j\)

二项式定理展开

\(\sum_{j=0}^{k} s_j\sum_{i=1}^n\sum_{l=0}^{j}\binom{j}{l} (ab^i)^l(cd^i)^{j-l}\)

\(\sum_{j=0}^{k} s_j\sum_{l=0}^{j}\binom{j}{l}\sum_{i=1}^n a^lb^ilc^{j-l}d^{i(j-l)}\)

\(\sum_{j=0}^{k} s_j\sum_{l=0}^{j}\binom{j}{l}a^lc^{j-l}\sum_{i=1}^n (b^ld^{j-l})^i\)

后面可以等比数列求和,然后直接做就行了

注意\(3\)和\(5\)在模\(998244353\)意义下没有二次剩余,所以把运算的数记为\(a+b\sqrt{c}\),然后写个类型,定义一下运算就好了

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<cmath>
#include<ctime>
#include<queue>
#include<map>
#include<set>
#define LL long long
#define db double using namespace std;
const int N=1000+20,mod=998244353;
LL rd()
{
LL x=0,w=1;char ch=0;
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
int fpow(int a,int b){int an=1;while(b){if(b&1) an=1ll*an*a%mod;a=1ll*a*a%mod,b>>=1;} return an;}
int inv(int a){return fpow(a,mod-2);}
int kk,s[N],fac[N],iac[N],bs;
int C(int n,int m){return m<0||n<m?0:1ll*fac[n]*iac[m]%mod*iac[n-m]%mod;}
struct node
{
int a,b;
node(){a=b=0;}
node(int na,int nb){a=na,b=nb;}
node operator + (const node &bb) const {return node((a+bb.a)%mod,(b+bb.b)%mod);}
node operator - (const node &bb) const {return node((a-bb.a+mod)%mod,(b-bb.b+mod)%mod);}
node operator * (const node &bb) const {return node((1ll*a*bb.a+1ll*b*bb.b%mod*bs)%mod,(1ll*a*bb.b+1ll*b*bb.a)%mod);}
node operator * (const LL &bb) const {return node(1ll*a*bb%mod,1ll*b*bb%mod);}
node invv(){int dt=inv((1ll*a*a-1ll*bs*b*b%mod+mod)%mod);return node(1ll*a*dt%mod,(mod-1ll*b*dt%mod)%mod);}
node operator ^ (const LL &bb) const
{
node an,a=*this;
an.a=1;
LL bbb=bb;
while(bbb)
{
if(bbb&1) an=an*a;
a=a*a,bbb>>=1;
}
return an;
}
}a,b,c,d,yi;
node dbsl(node aa,LL n){return aa.a==1&&aa.b==0?node(n%mod,0):(yi-(aa^n))*((yi-aa).invv());}
int sov(LL n)
{
int an=0;
for(int j=0;j<=kk;++j)
{
node sm;
for(int l=0;l<=j;++l)
sm=sm+(a^l)*(c^(j-l))*(dbsl((b^l)*(d^(j-l)),n+1)-yi)*C(j,l);
an=(an+1ll*s[j]*sm.a%mod)%mod;
}
return 1ll*an*iac[kk]%mod;
} int main()
{
yi.a=1;
fac[0]=1;
for(int i=1;i<=N-10;++i) fac[i]=1ll*fac[i-1]*i%mod;
iac[N-10]=inv(fac[N-10]);
for(int i=N-10;i;--i) iac[i-1]=1ll*iac[i]*i%mod;
int T=rd(),op=rd();
if(op==2)
{
bs=5;
a=node(inv(2),inv(10)),b=node(inv(2),inv(2)),c=node(inv(2),mod-inv(10)),d=node(inv(2),mod-inv(2));
}
else
{
bs=3;
a=node(inv(2),mod-inv(6)),b=node(2,1),c=node(inv(2),inv(6)),d=node(2,mod-1);
}
while(T--)
{
LL l=rd(),r=rd(),ln=(r-l+1)%mod;kk=rd();
if(op==3) l=(l+1)/2+1,r=r/2+1;
memset(s,0,sizeof(int)*(kk+3));
s[0]=1;
for(int i=0;i<kk;++i)
{
for(int j=kk;j;--j) s[j]=(1ll*s[j]*(mod-i)%mod+s[j-1])%mod;
s[0]=1ll*s[0]*(mod-i)%mod;
}
printf("%lld\n",1ll*(sov(r)-sov(l-1)+mod)%mod*inv(ln)%mod);
}
return 0;
}