P5641 【CSGRound2】开拓者的卓识

P5641 【CSGRound2】开拓者的卓识

像我这种菜鸡只能从组合意义考虑了,还要感谢 (color{black}{ exttt{c}}color{red}{ exttt{yn2006}}) 的指导。

题目的公式比较鬼畜,考虑从 (a_i)(sum_{k,1,r}) 的贡献入手考虑。

相当于我们要找有多少种“套娃”一样的区间包括 (i) 并且最大的那个是 ([1,r])

(1le l_{k}le l_{k}lecdots icdotsle r_{k-2}le r_{k-1}le r) 的不同 ({[l_1,r_1],[l_2,r_2],cdots,[l_{k},r_{k}]}) 有几个

显然左右端点是独立的。

所以贡献为 (a_i imesinom{i+k-2}{k-1}inom{r-i+k-1}{k-1})

意思就是每次左端点可以选择留在原地或者往左走,最多往左走 (i-1) 步。直接插板即可。

最好先观察一下,不要直接暴力拆阶乘。

发现下指标全是 (k-1) ,那么设 (p_i=inom{k-1+i}{k-1}),这个可以递推,考虑 (inom{n-1}{m} oinom{n}{m}) 的变化即可,即乘上 (dfrac{n}{n-m})

所以

(ans_i=sum_{j=1}^{i}a_j p_{j-1}p_{i-j})

可以卷了。

const int N=100005;
const int M=600005;
const int mod=998244353;
int n,k,a[M],p[M],inv[N],rev[M],lg,lim;
int qpow(int n,int k){int res=1;for(;k;k>>=1,n=1ll*n*n%mod)if(k&1)res=1ll*res*n%mod;return res;}
void init(int n){
	for (lg = 0, lim = 1; lim < n; ++ lg, lim <<= 1);
	for (int i = 0; i < lim; ++ i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (lg - 1));
}
void NTT(int*a,int g,int op=0){
	for (int i = 0; i < lim; ++ i) if (i > rev[i]) std::swap(a[i], a[rev[i]]);
	for (int i = 1; i < lim; i <<= 1) {
		int wn = qpow (g, (mod - 1) / (i << 1));
		for (int j = 0; j < lim; j += i << 1) {
			int w0 = 1; 
			for (int k = 0; k < i; ++ k, w0 = 1ll * w0 * wn % mod) {
				int X = a[j + k], Y = 1ll * w0 * a[i + j + k] % mod;
				a[j + k] = (X + Y) % mod, a[i + j + k] = (X - Y + mod) % mod;
			}
		}
	}
	if(!op)return;int ilim = qpow(lim, mod - 2);
	for (int i = 0; i < lim; ++ i) a[i] = 1ll * a[i] * ilim % mod;
}
signed main(){
	n=read(),k=read();
	for(int i=1;i<=n;++i)a[i]=read();
	p[0]=1,inv[1]=1;
	for(int i=2;i<=n;++i)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(int i=1;i<=n;++i)p[i]=1ll*p[i-1]*(k-1+i)%mod*inv[i]%mod,a[i]=1ll*a[i]*p[i-1]%mod;
	init(n<<1),NTT(a,3),NTT(p,3);
	for(int i=0;i<lim;++i)a[i]=1ll*a[i]*p[i]%mod;
	NTT(a,qpow(3,mod-2),1);
	for(int i=1;i<=n;++i)printf("%d ",a[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/zzctommy/p/14191409.html