[XOGT D]Do Use FFT

Do Use FFT

题解

调了好久。

直接暴力明显是不好的,我们考虑将上式转化一下。
F k ( x ) = ∏ i = 1 k ( x + B i ) , G ( x ) = ∑ i = 1 n C i 1 − A i x F_{k}(x)=\prod_{i=1}^{k}(x+B_{i}),G(x)=\sum_{i=1}^{n}\frac{C_{i}}{1-A_{i}x} Fk(x)=i=1k(x+Bi),G(x)=i=1n1AixCi
很明显,答案 a n s k = [ x 0 ] F k ( 1 x ) G ( x ) ans_{k}=[x^0]F_{k}(\frac{1}{x})G(x) ansk=[x0]Fk(x1)G(x)

对于 G ( x ) G(x) G(x),我们是可以通过简单的分治FFT求出来的。
由于对于每次加法,我们都要进行一次通分,所以我们需要分治来分别求出它的分子与分母。
最后再将分母求个逆,乘起来算出 G ( x ) G(x) G(x)

那么对于每个 F k ( x ) F_{k}(x) Fk(x)的求解,我们也可以采用分治的形式。
先通过线段树维护出不同段 ( 1 x + B i ) (\frac{1}{x}+B_{i}) (x1+Bi)的积,向左分治的时候就直接将当前的 G ( x ) G(x) G(x)传下去,向右分治的时候将 G ( x ) G(x) G(x)乘上左段的积往下传。
很明显,当我们下传到区间 [ l , r ] [l,r] [l,r],当前的多项式系数最多只用保留到 r − l + 1 r-l+1 rl+1,再往后的都不可能对 [ x 0 ] [x^0] [x0]的系数产生贡献了,所以这样分治可以有效的降低分治的时间复杂度。
最后到每个点的时候乘了当前点后直接加上第0项系数即可。

时间复杂度 O ( n l o g 2 n ) O\left(nlog^2n\right) O(nlog2n)

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
#include<map>
using namespace std;
#define MAXN 600005
#define lowbit(x) (x&-x)
#define reg register
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> pii;
typedef vector<int> poly;
const int INF=0x7f7f7f7f;
const int mo=998244353;
const double PI=acos(-1.0);
const int orG=3,invG=332748118;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
int rev[MAXN<<2],ans[MAXN],n;
int add(const int x,const int y){return x+y<mo?x+y:x+y-mo;}
int qkpow(int a,int s){int t=1;while(s){if(s&1)t=1ll*t*a%mo;a=1ll*a*a%mo;s>>=1;}return t;}
void NTT(int lim,poly &A,int typ){
	for(int i=0;i<lim;i++)if(i<rev[i])swap(A[i],A[rev[i]]);
	for(int i=1;i<lim;i<<=1){
		int Wn=qkpow(typ^-1?orG:invG,(mo-1)/(i<<1));
		for(int j=0;j<lim;j+=(i<<1)){
			for(int k=0,W=1;k<i;k++,W=1ll*W*Wn%mo){
				int x=A[j+k],y=1ll*W*A[i+j+k]%mo;
				A[j+k]=add(x,y);A[i+j+k]=add(x,mo-y);
			}
		}
	}
	if(typ^-1)return ;const int iv=qkpow(lim,mo-2);
	for(int i=0;i<lim;i++)A[i]=1ll*A[i]*iv%mo;
}
int A[MAXN],B[MAXN],C[MAXN];
void getRev(int lim,int L){for(int i=0;i<lim;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<L-1);}
poly operator * (poly X,poly Y){
	int len=X.size()+Y.size()-1,lim=1,L=0;
	while(lim<len)lim<<=1,L++;X.resize(lim);Y.resize(lim);getRev(lim,L);
	NTT(lim,X,1);NTT(lim,Y,1);for(int i=0;i<lim;i++)X[i]=1ll*X[i]*Y[i]%mo;NTT(lim,X,-1);
	X.resize(len);return X;
}
poly operator + (poly X,poly Y){
	if(X.size()<Y.size())X.swap(Y);
	for(int i=0;i<Y.size();i++)X[i]=add(X[i],Y[i]);
	return X;
}
poly polyinv(poly X){
	if(X.size()==1){poly res;res.clear();res.push_back(qkpow(X[0],mo-2));return res;};
	int lim=1,L=0,len=X.size()+1LL>>1LL;poly Y=polyinv(poly(X.begin(),X.begin()+len));
	len=X.size();while(lim<(len<<1LL))lim<<=1LL,L++;getRev(lim,L);
	X.resize(lim);Y.resize(lim);NTT(lim,X,1);NTT(lim,Y,1);
	for(int i=0;i<lim;i++)Y[i]=1ll*add(2LL,mo-1ll*X[i]*Y[i]%mo)*Y[i]%mo;
	NTT(lim,Y,-1);Y.resize(len);return Y;
}
pair<poly,poly> sakura(int l,int r){
	if(l==r){
		poly tmp1;tmp1.clear();tmp1.push_back(C[l]);
		poly tmp2;tmp2.clear();tmp2.push_back(1);tmp2.push_back(mo-A[l]);
		return make_pair(tmp1,tmp2);
	}
	int mid=l+r>>1;pair<poly,poly> tL=sakura(l,mid),tR=sakura(mid+1,r);
	return make_pair(tL.fir*tR.sec+tL.sec*tR.fir,tL.sec*tR.sec);
}
struct SegmentTree{
	poly tr[MAXN<<2];
	void updata(int rt){tr[rt]=tr[rt<<1]*tr[rt<<1|1];}
	void build(int rt,int l,int r){
		if(l==r){tr[rt].clear();tr[rt].push_back(B[l]);tr[rt].push_back(1);return ;}
		int mid=l+r>>1; build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);updata(rt);
	}
	void dosaka(int rt,int l,int r,poly tmp){
		if(l==r)return (void)(ans[l]=add(tmp[1],1ll*tmp[0]*B[l]%mo));
		int mid=l+r>>1;dosaka(rt<<1,l,mid,poly(tmp.begin(),tmp.begin()+mid-l+2));
		reverse(tr[rt<<1].begin(),tr[rt<<1].end());tmp=tmp*tr[rt<<1];
		dosaka(rt<<1|1,mid+1,r,poly(tmp.begin()+tr[rt<<1].size()-1,tmp.begin()+tr[rt<<1].size()+r-mid));
	}
}T;
signed main(){
	read(n);
	for(int i=0;i<n;i++)read(A[i]);
	for(int i=0;i<n;i++)read(B[i]);
	for(int i=0;i<n;i++)read(C[i]);
	pair<poly,poly> F=sakura(0,n-1);
	poly H=polyinv(F.sec);poly G=F.fir*H;
	T.build(1,0,n-1);T.dosaka(1,0,n-1,G);
	for(int i=0;i<n;i++)printf("%d ",ans[i]);
	return 0;
}

谢谢!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值