ARC118F Growth Rate

给出个长度为\(n\)的序列\(A_i\),问有多少个长度为\(n+1\)的序列\(X_i\),满足:

  1. \(X_i\in [1,m]\)
  2. \(A_iX_i\le X_{i+1}\)

\(n\le 1000,m\le 10^{18},\prod A_i\le M\)


首先有个暴力:从后往前做,设\(f_k(x)\)表示第\(k\)位选择\(x\)的方案数是什么。

转移:\(f_k(x)=\sum_{y\ge ax} f_{k+1}(y)\)

结论:\(f_k(x)\)是个关于\(x\)的多项式,定义域为\([1,R]\)\(R\)为原题意中于\(k\)位置能合法放置的最大数。原因根据以下做法,归纳就能说明。

假设已经知道了\(f_k(y)=\sum_i a_i y^i\)

\(F_k(x)\)表示\(f_k(x)\)的前缀和,即\(F_{k}(x)=\sum_{y\le x} f_k(y)=\sum_i a_i \sum_{y\le x}y^i\),后面是个自然数幂和,所以\(F_k(x)\)也是多项式。

根据转移,\(f_{k+1}(x)=F_k(R)-F_k(ax-1)\)。显然\(f_{k+1}(x)\)也是多项式。

现在问题是两个:给多项式做前缀和;给多项式复合\(ax-1\)

先讲题解做法:

考虑维护若干个点值(假设\(c\)个,\(c=O(n)\)),分别为\(1,2,\dots c\)

前缀和:直接用点值前缀和。时间\(O(n)\)

复合:对于每个点\(x_i\),通过拉格朗日插值查\(F_k(ax_i-1)\)。时间\(O(n^2)\)

\(A_k>1\)时直接\(O(n^2)\)复合,次数\(O(\lg m)\)次;\(A_k=1\)时特殊处理:前缀和就直接前缀和,后面是\(F_k(R)-F_k(x-1)\)\(F_k(x-1)\)已知可以直接用。于是时间\(O(n)\)

于是总时间\(O(n^2\lg m)\)

我的口胡做法:

考虑对前缀和进行一波多项式推导(以下\(B\)为伯努利数\(B^+\)):

\[\sum_i a_i \sum_{y\le x}y^i\\ =\sum_i a_i \frac{1}{i+1}\sum_{j=1}^{i+1}\binom{i+1}{j}B_{i+1-j}x^{j}\\ =\sum_{j\ge 1}x^{j}\sum_{i+1\ge j}a_i \frac{1}{i+1}\binom{i+1}{j}B_{i+1-j}\\ =\sum_{j\ge 1}\frac{x^{j}}{j!}\sum_{i+1\ge j}(a_ii!) \frac{B_{i+1-j}}{(i+1-j)!}\\ \]

可以发现最后是个减法卷积的形式。

再对复合进行一波多项式推导:

\[F(ax-1)\\ =\sum_i F_i(ax-1)^i\\ =\sum_i F_i\sum_{j=0}^ia^jx^j(-1)^{i-j}\binom{i}{j}\\ =\sum_{j}a^jx^j(-1)^{j}\sum_{i\ge j} F_i\binom{i}{j}(-1)^i\\ =\sum_{j}a^jx^j\frac{(-1)^{j}}{j!}\sum_{i\ge j} \left(F_ii!(-1)^i\right)\left(\frac{1}{(i-j)!}\right) \]

也是个减法卷积的形式。

于是全程用多项式做,可以搞到\(O(n^2 \lg n)\)的复杂度。


using namespace std;
#include <bits/stdc++.h>
#define N 1005
#define ll long long
#define mo 998244353
ll qpow(ll x,ll y=mo-2){
	ll r=1;
	for (;y;y>>=1,x=x*x%mo)
		if (y&1)
			r=r*x%mo;
	return r;
}
int n,k;
ll m,a[N];
ll d[N],R;
ll fac[N],ifac[N];
void initC(int n){
	fac[0]=1;
	for (int i=1;i<=n;++i)
		fac[i]=fac[i-1]*i%mo;
	ifac[n]=qpow(fac[n]);
	for (int i=n-1;i>=0;--i)
		ifac[i]=ifac[i+1]*(i+1)%mo;
}
ll ask(ll z){
	if (z>R || z<1) return 0;
	z%=mo;
	ll y=0,pre=1;
	static ll suc[N];
	suc[k+1]=1;
	for (int x=k;x>=1;--x)
		suc[x]=suc[x+1]*(z-x)%mo;
	for (int x=1;x<=k;++x){
		ll tmp=pre*suc[x+1]%mo;
		(y+=d[x]*tmp%mo*ifac[k-x]%mo*ifac[x-1]*(k-x&1?-1:1))%=mo;
		pre=pre*(z-x)%mo;
	}
	return (y+mo)%mo;
}
ll C(ll m,ll n){
	ll s=1;
	for (ll i=m-n+1;i<=m;++i)
		s=s*i%mo;
	for (ll i=1;i<=n;++i)
		s=s*qpow(i)%mo;
	return s;
}
int main(){
	//freopen("in.txt","r",stdin);
	scanf("%d%lld",&n,&m);
	for (int i=1;i<=n;++i)
		scanf("%lld",&a[i]);
	k=n+2;
	initC(k);
	R=m;
	for (int x=1;x<=k;++x)
		d[x]=min((ll)x,m);
	m%=mo;
	for (int i=n;i>=1;--i){
		ll sum=ask(R);
		if (a[i]==1){
			for (int x=k;x>=1;--x)
				d[x]=(sum-d[x-1]+mo)%mo;
		}
		else{
			static ll t[N];
			for (int x=k;x>=1;--x)
				t[x]=(sum-ask(a[i]*x-1)+mo)%mo;
			memcpy(d,t,sizeof(ll)*(k+1));			
		}
		R=R/a[i];
		for (int x=1;x<=k;++x)
			d[x]=(d[x]+d[x-1]+mo)%mo;
	}
	ll ans=ask(R);
	printf("%lld\n",ans);
	return 0;
}
posted @ 2021-05-13 21:09  jz_597  阅读(234)  评论(0编辑  收藏  举报