Chirp Z-Transform

给定多项式 \(f(x) = \sum\limits_{i=0}^{n-1} a_i x^i\),求 \(f(c^0),f(c^1),\dots,f(c^{m-1})\),对 \(998244353\) 取模。

\(1 \leq n,m \leq 10^6\),\(0 \leq c,a_i < 998244353\)。

我们得到 \(f(c^j) = \sum\limits_{i=0}^{n-1}a_i c^{ij}\)。

接下来考虑 \(ij = \dbinom{i+j}{2} - \dbinom{i}{2} - \dbinom{j}{2}\),证明可以使用组合意义。

也就是说,\(f(c^j) \times c^{\binom{j}{2}} = \sum\limits_{i=0}^{n-1} a_i c^{-\binom{i}{2}} \times c^{\binom{i+j}{2}}\)。

考虑如何计算右式,自然的发现其具有卷积的形式。

于是我们令 \(g_i\) 的系数为 \(a_i c^{-\binom{i}{2}}\),\(h_i\) 的系数为 \(c^{\binom{i}{2}}\),则右式计算的就是 \(g_j\) 和 \(h_{i+j}\) 系数乘积之和。

等等,好像不太对,我们发现这个东西是减法,而卷积是加法。自然的,修改 \(g_i\) 的定义,令 \(g_i\) 的系数为 \(a_{-i} c^{-\binom{-i}{2}}\),\(h_i\) 的系数为 \(c^{\binom{i}{2}}\),则右式计算的就是 \(g_{-i}\) 和 \(h_{i+j}\) 系数乘积之和。

等等,好像还是不太对,你发现 \(g_{-i}\) 下标是负数,考虑整体向右平移 \(n\),令 \(g_{i}\) 的系数为 \(a_{n-i}c^{-\binom{n-i}{2}}\),\(h_i\) 的系数为 \(c^{\binom{i}{2}}\),现在计算的终于是 \(g_{n-i}\) 和 \(h_{i+j}\) 系数乘积之和了。

你考虑 \(g\) 是 \(n\) 次的,\(h\) 是 \(m+n\) 次的,空间不能开小。

#include<iostream>
#include<cstdio>
using namespace std;
const int mod=998244353,root=15311432,inv_2=499122177;
const int L=23,N=1<<22;
int omega[1<<L],inv_omega[1<<L];
void init(){
	omega[0]=1;
	for(int i=1;i<(1<<L);i++){
		omega[i]=(long long)omega[i-1]*root%mod;
	}
	inv_omega[0]=1;
	for(int i=1;i<(1<<L);i++){
		inv_omega[i]=omega[(1<<L)-i];
	}
}
int f[N],g[N],h[N],t;
int buf[N];
void NTT(int* a){
	for(int i=t;i>=1;i--){
		for(int j=0;j<(1<<t);j+=(1<<i)){
			for(int k=0;k<(1<<i-1);k++){
				buf[j+k]=a[j+k*2];
				buf[j+k+(1<<i-1)]=a[j+k*2+1];
			}
			for(int k=0;k<(1<<i);k++){
				a[j+k]=buf[j+k];
			}
		}
	}
	for(int i=1;i<=t;i++){
		for(int j=0;j<(1<<t);j+=(1<<i)){
			for(int k=0;k<(1<<i-1);k++){
				buf[j+k]=(a[j+k]+(long long)omega[k<<L-i]*a[j+k+(1<<i-1)]%mod+mod)%mod;
				buf[j+k+(1<<i-1)]=(a[j+k]-(long long)omega[k<<L-i]*a[j+k+(1<<i-1)]%mod+mod)%mod;
			}
			for(int k=0;k<(1<<i);k++){
				a[j+k]=buf[j+k];
			}
		}
	}
}
void INTT(int* a){
	for(int i=t;i>=1;i--){
		for(int j=0;j<(1<<t);j+=(1<<i)){
			for(int k=0;k<(1<<i-1);k++){
				buf[j+k]=((long long)(a[j+k]+a[j+k+(1<<i-1)])+mod)%mod*inv_2%mod;
				buf[j+k+(1<<i-1)]=((long long)(a[j+k]-a[j+k+(1<<i-1)])+mod)%mod*inv_2%mod*inv_omega[k<<L-i]%mod;
			}
			for(int k=0;k<(1<<i);k++){
				a[j+k]=buf[j+k];
			}
		}
	}
	for(int i=1;i<=t;i++){
		for(int j=0;j<(1<<t);j+=(1<<i)){
			for(int k=0;k<(1<<i-1);k++){
				buf[j+k*2]=a[j+k];
				buf[j+k*2+1]=a[j+k+(1<<i-1)];
			}
			for(int k=0;k<(1<<i);k++){
				a[j+k]=buf[j+k];
			}
		}
	}
}
long long A[N];
long long pow_mod(long long num1,long long num2=mod-2){
	num1=(num1%mod+mod)%mod;
	num2=(num2%(mod-1)+(mod-1))%(mod-1);
	long long num3=1;
	while(num2){
		if(num2&1){
			num3=num3*num1%mod;
		}
		num1=num1*num1%mod;
		num2>>=1;
	}
	return num3;
}
int main(){
	init();
	int n,m;
	long long c;
	scanf("%d %lld %d",&n,&c,&m);
	for(int i=0;i<=n-1;i++){
		scanf("%lld",&A[i]);
	}
	for(int i=1;i<=n;i++){
		g[i]=A[n-i]*pow_mod(c,-(long long)(n-i)*(n-i-1)/2)%mod;
	}
	for(int i=0;i<=n+m-2;i++){
		h[i]=pow_mod(c,(long long)i*(i-1)/2);
	}
	while((1<<t)<=n+n+m-2){
		t++;
	}
	NTT(g);
	NTT(h);
	for(int i=0;i<(1<<t);i++){
		f[i]=(long long)g[i]*h[i]%mod;
	}
	INTT(f);
	for(int i=0;i<=m-1;i++){
		long long pre=pow_mod(c,-(long long)i*(i-1)/2);
		printf("%lld ",pre*f[n+i]%mod);
	}
	return 0;
}
posted @ 2026-01-06 23:30  Oken喵~  阅读(15)  评论(0)    收藏  举报