6693. 【2020.06.05省选模拟】紫色彼岸樱推迟绽放

题目

题意不说了,有点费笔墨。


思考历程

想着之前练习了很久的生成函数,这次终于能试验一下了。
然后我推啊推啊推了半天最后退出了一个类似于斐波拉契数列的递推式。
这个不是DP地想不就可以出来了么……
(其实当初如果再进一步将那个生成函数的分式展开,或许就可以发现新东西了)。


正解

正解就是乱推式子。
首先题目可以写成这个东西: ∑ i = D D + T − 1 C i L ∑ j = 0 K − 1 i j A K − 1 − j C K − 1 − j j \sum_{i=D}^{D+T-1}C_{i}^L\sum_{j=0}^{K-1}i^jA^{K-1-j}C_{K-1-j}^j i=DD+T1CiLj=0K1ijAK1jCK1jj
至于怎么理解:后面 K − 1 K-1 K1段相当于在若干个 A A A中插入一些 i i i,一个空至多插一个。
后面大段式子可以表示成 F ( i ) F(i) F(i)
于是式子变成了 1 L ! ∑ i = D D + T − 1 i ! ( i − L ) ! F ( i ) \frac{1}{L!}\sum_{i=D}^{D+T-1}\frac{i!}{(i-L)!}F(i) L!1i=DD+T1(iL)!i!F(i)
尝试去把 i ! i! i!抵消掉。
神奇的变换:将 F ( x ) F(x) F(x)写成上升幂多项式, F ( x ) = ∑ i = 0 K − 1 a i ( x + 1 ) i ‾ = ∑ i = 0 K − 1 a i ( x + i ) ! x ! F(x)=\sum_{i=0}^{K-1}a_i(x+1)^{\overline{i}}=\sum_{i=0}^{K-1}a_i\frac{(x+i)!}{x!} F(x)=i=0K1ai(x+1)i=i=0K1aix!(x+i)!

是可以证明一定存在满足条件的 a i a_i ai的,但是我不会具体证明。
大概理解一下:将整条式子展开之后会变成一个 K − 1 K-1 K1次的多项式,每项的系数是若干个 a i a_i ai线性地相加。
插值之后一定可以解出每个系数是多少,然后再高斯消元就可以解出每个 a i a_i ai的具体值。
(至于会不会消出自由元我就不会证了)

继续推式子: 1 L ! ∑ i = D D + T − 1 i ! ( i − L ) ! ∑ j = 0 K − 1 a j ( i + j ) ! i ! = 1 L ! ∑ j = 0 K − 1 a j ∑ i = D D + T − 1 ( i + j ) ! ( i − L ) ! \frac{1}{L!}\sum_{i=D}^{D+T-1}\frac{i!}{(i-L)!}\sum_{j=0}^{K-1}a_j\frac{(i+j)!}{i!}=\frac{1}{L!}\sum_{j=0}^{K-1}a_j\sum_{i=D}^{D+T-1}\frac{(i+j)!}{(i-L)!} L!1i=DD+T1(iL)!i!j=0K1aji!(i+j)!=L!1j=0K1aji=DD+T1(iL)!(i+j)!
看它不爽给它配个组合数: 1 L ! ∑ j = 0 K − 1 a j ( j + L ) ! ∑ i = D D + T − 1 C i + j j + L = 1 L ! ∑ j = 0 K − 1 a j ( j + L ) ! ( C D + T + j j + L + 1 − C D + j j + L + 1 ) \frac{1}{L!}\sum_{j=0}^{K-1}a_j(j+L)!\sum_{i=D}^{D+T-1}C_{i+j}^{j+L}=\frac{1}{L!}\sum_{j=0}^{K-1}a_j(j+L)!(C_{D+T+j}^{j+L+1}-C_{D+j}^{j+L+1}) L!1j=0K1aj(j+L)!i=DD+T1Ci+jj+L=L!1j=0K1aj(j+L)!(CD+T+jj+L+1CD+jj+L+1)
(最后的那个变换是经典套路,具体不赘述(记得用第二类斯特林数推自然数幂和的时候用过这条式子))
于是如果我们知道了 a i a_i ai,就可以在 O ( k ) O(k) O(k)内算出这条式子。

F ( x ) = ∑ i = 0 K − 1 a i ( x + i ) ! x ! = ∑ i = 0 K − 1 a i i ! C x + i i F(x)=\sum_{i=0}^{K-1}a_i\frac{(x+i)!}{x!}=\sum_{i=0}^{K-1}a_ii!C_{x+i}^i F(x)=i=0K1aix!(x+i)!=i=0K1aii!Cx+ii
假如 a > 0 , b ≥ 0 a>0,b\geq 0 a>0,b0,那么有 C − a b = C a + b − 1 b ( − 1 ) b C_{-a}^b=C_{a+b-1}^b(-1)^b Cab=Ca+b1b(1)b
于是脑洞PTY将 − 1 , − 2 , . . . , − K -1,-2,...,-K 1,2,...,K代进去(记为 − n − 1 -n-1 n1),得到 F ( − n − 1 ) = ∑ i = 0 K − 1 a i i ! C − n − 1 + i i = ∑ i = 0 K − 1 a i i ! C n i ( − 1 ) i = ∑ i = 0 n a i i ! C n i ( − 1 ) i F(-n-1)=\sum_{i=0}^{K-1}a_ii!C_{-n-1+i}^i=\sum_{i=0}^{K-1}a_ii!C_{n}^i(-1)^i=\sum_{i=0}^{n}a_ii!C_{n}^i(-1)^i F(n1)=i=0K1aii!Cn1+ii=i=0K1aii!Cni(1)i=i=0naii!Cni(1)i
发现是个二项式反演: a n n ! = ∑ i = 0 n F ( − n − 1 ) C n i ( − 1 ) i a_nn!=\sum_{i=0}^nF(-n-1)C_n^i(-1)^i ann!=i=0nF(n1)Cni(1)i
展开后发现是个卷积。
于是如果算出 F ( − n − 1 ) F(-n-1) F(n1),就可以算出 a i a_i ai了。

回归原式:
F ( x ) = ∑ i = 0 K − 1 x i A K − 1 − i C K − 1 − i i F(x)=\sum_{i=0}^{K-1}x^iA^{K-1-i}C_{K-1-i}^i F(x)=i=0K1xiAK1iCK1ii
组合意义就是在若干个 A A A中插入 x x x,使得总长度为 K − 1 K-1 K1,每个空只能插一个 x x x(最前面那个空不能插)。
这个东西可以递推来求,递推式长得像斐波拉契数列。
然后就可以矩阵乘法,在 O ( lg ⁡ K ) O(\lg K) O(lgK)时间内计算。

至此这个大推式子题结束了。


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 20000000
#define maxK 262144
#define ll long long
#define mo 998244353
ll qpow(ll x,int y=mo-2){
	ll r=1;
	for (;y;y>>=1,x=x*x%mo)
		if (y&1)
			r=r*x%mo;
	return r;
}
int fac[N],ifac[N];
ll C(int m,int n){return m>=n?(ll)fac[m]*ifac[n]%mo*ifac[m-n]%mo:0;}
int K,A,L,D,T;
int nN,re[maxK];
void dft(ll A[],int flag){
	for (int i=0;i<nN;++i)
		if (i<re[i])
			swap(A[i],A[re[i]]);
	for (int i=1;i<nN;i<<=1){
		ll wn=qpow(3,flag==1?(mo-1)/(2*i):mo-1-(mo-1)/(2*i));
		for (int j=0;j<nN;j+=i<<1){
			ll wnk=1;
			for (int k=j;k<j+i;++k,wnk=wnk*wn%mo){
				ll x=A[k],y=wnk*A[k+i]%mo;
				A[k]=(x+y)%mo;
				A[k+i]=(x-y+mo)%mo;
			}
		}
	}
	if (flag==-1){
		ll inv=qpow(nN);
		for (int i=0;i<nN;++i)
			A[i]=A[i]*inv%mo;
	}
}
void multi(ll C[],ll A[],ll B[]){
	int bit=0;
	for (nN=1;nN<2*K;nN<<=1,++bit);
	re[0]=0;
	for (int i=1;i<nN;++i)
		re[i]=re[i>>1]>>1|(i&1)<<bit-1;
	dft(A,1),dft(B,1);
	for (int i=0;i<nN;++i)
		C[i]=A[i]*B[i]%mo;
	dft(C,-1);
}
ll b[maxK],c[maxK],a[maxK];
struct Matrix{
	int m[2][2];
} Tr;
void operator*=(Matrix &a,Matrix b){
	static Matrix c;
	for (int i=0;i<2;++i)
		for (int j=0;j<2;++j)
			c.m[i][j]=((ll)a.m[i][0]*b.m[0][j]+(ll)a.m[i][1]*b.m[1][j])%mo;
	memcpy(&a,&c,sizeof c);
}
void getpow(Matrix &x,int y){
	static Matrix r;
	r={1,0,0,1};
	for (;y;y>>=1,x*=x)
		if (y&1)
			r*=x;
	memcpy(&x,&r,sizeof r);
}
ll f(int x){
	if (K==2)
		return A;
	x=(x+mo)%mo;
	Tr={0,int((ll)x*A%mo),1,A};
	getpow(Tr,K-1-2);
	ll g1=A,g2=(ll)A*A%mo;
	return ((g1*Tr.m[0][0]+g2*Tr.m[1][0])%mo*x+g1*Tr.m[0][1]+g2*Tr.m[1][1])%mo;
}
int main(){
	freopen("bloom.in","r",stdin);
	freopen("bloom.out","w",stdout);
	fac[0]=1;
	for (int i=1;i<=N;++i)
		fac[i]=(ll)fac[i-1]*i%mo;
	ifac[N]=qpow(fac[N]);
	for (int i=N-1;i>=0;--i)
		ifac[i]=(ll)ifac[i+1]*(i+1)%mo;
	int Q;
	scanf("%d%d%d",&K,&A,&Q);
	for (int i=0;i<K;++i){
		b[i]=f(-i-1)*(i&1?mo-1:1)%mo*ifac[i]%mo;
		c[i]=ifac[i];
	}
	multi(a,b,c);
	while (Q--){
		scanf("%d%d%d",&L,&D,&T);
		ll ans=0;
		for (int j=0;j<K;++j)
			ans=(ans+(ll)a[j]*fac[j+L]%mo*(C(D+T+j,j+L+1)-C(D+j,j+L+1)+mo))%mo;
		ans=ans*ifac[L]%mo;
		printf("%lld\n",ans);
	}
	return 0;
}

总结

%%%PTY
感觉自己的推式子能力有待加强……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值