Learning:多项式(三)一些NTT的扩展

任意模数FFT

如果模数不是一些NTT的模数,那我们又如何解决呢?(当然用拆位FFT也可以做,但是我没写过)
首先我们知道有个东西叫中国剩余定理。
我们可以选取多个模数先用NTT求一下,然后再用中国剩余定理合并。一般取三个模数。
但是要注意的一点,我们在合并的时候可能会炸long long,所以我们需要使用快速乘来解决。
模板:

#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
const long long p[3]={998244353,1004535809,469762049},g=3,gi[3]={332748118,334845270,156587350},M=p[0]*p[1];
int n,m,mod,limit=1,l,r[4000010];
long long a[3][400010],b[3][400010];
int rd(){
    int x=0;
    char c;
    do c=getchar();
    while(!isdigit(c));
    do{
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }while(isdigit(c));
    return x;
}
long long qpow(long long x,int n,long long mod){
    long long ans=1;
    while(n){
        if(n&1)
            ans=(ans*x)%mod;
        x=(x*x)%mod;
        n>>=1;
    }
    return ans;
}
void NTT(int limit,long long *a,int type,int idx){
    for(int i=0;i<limit;i++)
        if(i<r[i])
            swap(a[i],a[r[i]]);
    for(int mid=1;mid<limit;mid<<=1){
        long long wn;
        if(type==1)
            wn=qpow(g,(p[idx]-1)/(mid<<1),p[idx]);
        else wn=qpow(gi[idx],(p[idx]-1)/(mid<<1),p[idx]);
        for(int i=0;i<limit;i+=(mid<<1)){
            long long w=1;
            for(int j=0;j<mid;j++,w=w*wn%p[idx]){
                int x=a[i+j],y=w*a[i+j+mid]%p[idx];
                a[i+j]=(x+y)%p[idx];
                a[i+j+mid]=(x-y+p[idx])%p[idx];
            }
        }
    }
    if(~type)
        return;
    long long inv=qpow(limit,p[idx]-2,p[idx]);
    for(int i=0;i<=n+m;i++)
        a[i]=(a[i]*inv)%p[idx];
    return;
}
long long mul(long long a,long long b,long long mod){
    a%=mod;
    b%=mod;
    long long d=(long long)(a*(double)b/mod+0.5);
    long long ret=a*b-d*mod;
    if(ret<0)
        return ret+mod;
    return ret;
}
int main(){
    n=rd();
    m=rd();
    mod=rd();
    for(int i=0;i<=n;i++){
        a[0][i]=a[1][i]=a[2][i]=rd();
        a[0][i]%=p[0];
        a[1][i]%=p[1];
        a[2][i]%=p[2];
    }
    for(int i=0;i<=m;i++){
        b[0][i]=b[1][i]=b[2][i]=rd();
        b[0][i]%=p[0];
        b[1][i]%=p[1];
        b[2][i]%=p[2];
    }
    while(limit<=n+m){
        limit<<=1;
        l++;
    }
    for(int i=0;i<limit;i++)
        r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
    for(int i=0;i<3;i++){
        NTT(limit,a[i],1,i);
        NTT(limit,b[i],1,i);
        for(int j=0;j<limit;j++)
            a[i][j]=(a[i][j]*b[i][j])%p[i];
        NTT(limit,a[i],-1,i);
    }
    long long ni1=qpow(p[1]%p[0],p[0]-2,p[0]),ni2=qpow(p[0]%p[1],p[1]-2,p[1]),ni3=qpow(M%p[2],p[2]-2,p[2]);
    for(int i=0;i<=n+m;i++){
        long long A=(mul(a[0][i]*p[1],ni1,M)+mul(a[1][i]*p[0],ni2,M))%M,k=((a[2][i]-A)%p[2]+p[2])%p[2]*ni3%p[2];
        printf("%lld ",(k%mod*(M%mod)%mod+A%mod)%mod);
    }
    return 0;
}
注意:以下部分代码中的 n n n均已补成 2 2 2的幂了。

多项式求逆

已知多项式 A ( x ) A(x) A(x),求一个多项式 B ( x ) B(x) B(x),满足: A ( x ) B ( x ) ≡ 1 ( m o d x n ) A(x)B(x)\equiv1\pmod{x^n} A(x)B(x)1(modxn)
我们先考虑 n = 1 n=1 n=1的情况,此时直接将多项式 A ( x ) A(x) A(x)的常数项求个逆元即可。
假设我们已经求出了满足 A ( x ) B ( x ) ≡ 1 ( m o d x n 2 ) A(x)B(x)\equiv1\pmod {x^{\frac{n}{2}}} A(x)B(x)1(modx2n) B ( x ) B(x) B(x)
移项得: 0 ≡ 1 − A ( x ) B ( x ) ( m o d x n 2 ) 0\equiv1-A(x)B(x)\pmod{x^{\frac{n}{2}}} 01A(x)B(x)(modx2n)
两边同时平方得: 0 ≡ 1 − 2 A ( x ) B ( x ) + A 2 ( x ) B 2 ( x ) ( m o d x n ) 0\equiv1-2A(x)B(x)+A^2(x)B^2(x)\pmod{x^n} 012A(x)B(x)+A2(x)B2(x)(modxn)
移项得: 2 A ( x ) B ( x ) − A 2 ( x ) B 2 ( x ) ≡ 1 ( m o d x n ) 2A(x)B(x)-A^2(x)B^2(x)\equiv1\pmod{x^n} 2A(x)B(x)A2(x)B2(x)1(modxn)
提公因式得: A ( x ) ( 2 B ( x ) − A ( x ) B 2 ( x ) ) ≡ 1 ( m o d x n ) A(x)(2B(x)-A(x)B^2(x))\equiv1\pmod{x^n} A(x)(2B(x)A(x)B2(x))1(modxn)
我们用NTT求出 2 B ( x ) − A ( x ) B 2 ( x ) 2B(x)-A(x)B^2(x) 2B(x)A(x)B2(x),就可以得到满足 A ( x ) B ( x ) ≡ 1 ( m o d x n ) A(x)B(x)\equiv1\pmod{x^n} A(x)B(x)1(modxn)的了 B ( x ) B(x) B(x)
递归求解即可。
代码:

void Inverse(int *a,int *b,int n){
	if(n==1){
		b[0]=qpow(a[0],mod-2);
		return;
	}
	Inverse(a,b,n>>1);
	for(int i=0;i<(n<<1);i++)
		r[i]=(r[i>>1]>>1)|(i&1?n:0);
	for(int i=0;i<n;i++)
		c[i]=a[i];
	for(int i=n;i<(n<<1);i++)
		c[i]=0;
	NTT(b,n<<1,1);
	NTT(c,n<<1,1);
	for(int i=0;i<(n<<1);i++)
		b[i]=(2ll*b[i]-1ll*b[i]*b[i]%mod*c[i]%mod+mod)%mod;
	NTT(b,n<<1,-1);
	for(int i=n;i<(n<<1);i++)
		b[i]=0;
	return;
}

多项式除法

已知 n n n次多项式 A ( x ) A(x) A(x) m m m次多项式 B ( x ) B(x) B(x),求满足 A ( x ) ≡ B ( x ) D ( x ) + R ( x ) A(x)\equiv B(x)D(x)+R(x) A(x)B(x)D(x)+R(x)的多项式 D ( x ) D(x) D(x) R ( x ) R(x) R(x)。其中多项式 D ( x ) D(x) D(x)的次数为 n − m n-m nm,多项式 R ( x ) R(x) R(x)的次数严格小于 m m m
对于一个多项式 A ( x ) A(x) A(x),我们记 A r ( x ) A^r(x) Ar(x)表示 A ( x ) A(x) A(x)的系数反转后的多项式(即如果 A ( x ) = ∑ i = 0 n a i x i A(x)=\sum_{i=0}^na_ix^i A(x)=i=0naixi,则 A r ( x ) = ∑ i = 0 n a n − i x i A^r(x)=\sum_{i=0}^na_{n-i}x^i Ar(x)=i=0nanixi。那么我们会发现: x n A ( 1 x ) = A r ( x ) x^nA(\frac{1}{x})=A^r(x) xnA(x1)=Ar(x)
我们把 1 x \frac{1}{x} x1代入原式就可以得到: A ( 1 x ) = B ( 1 x ) D ( 1 x ) + R ( 1 x ) A(\frac{1}{x})=B(\frac{1}{x})D(\frac{1}{x})+R(\frac{1}{x}) A(x1)=B(x1)D(x1)+R(x1)
然后就是: x n A ( 1 x ) = x n B ( 1 x ) D ( 1 x ) + x n R ( 1 x ) x^nA(\frac{1}{x})=x^nB(\frac{1}{x})D(\frac{1}{x})+x^nR(\frac{1}{x}) xnA(x1)=xnB(x1)D(x1)+xnR(x1)
x n A ( 1 x ) = x m B ( 1 x ) x n − m D ( 1 x ) + x n R ( 1 x ) x^nA(\frac{1}{x})=x^mB(\frac{1}{x})x^{n-m}D(\frac{1}{x})+x^nR(\frac{1}{x}) xnA(x1)=xmB(x1)xnmD(x1)+xnR(x1)
A r ( x ) = B r ( x ) D r ( x ) + x n − m + 1 R r ( x ) A^r(x)=B^r(x)D^r(x)+x^{n-m+1}R^r(x) Ar(x)=Br(x)Dr(x)+xnm+1Rr(x)
因为 D ( x ) D(x) D(x)的次数为 n − m n-m nm,所以我们可以在模 x n − m + 1 x^{n-m+1} xnm+1的意义下来搞,那么:
A r ( x ) ≡ B r ( x ) D r ( x ) ( m o d x n − m + 1 ) A^r(x)\equiv B^r(x)D^r(x)\pmod{x^{n-m+1}} Ar(x)Br(x)Dr(x)(modxnm+1)
我们会发现这个时候我们只需要一个求逆就可以求出 D r ( x ) D^r(x) Dr(x)了,然后我们反转一下代入原式就可以求出 R ( x ) R(x) R(x)了。
代码:

void Divide(int *a,int N,int *b,int M,int *d){
	for(int i=0;i<=N;i++)
		tmp2[i]=a[i];
	for(int i=0;i<=M;i++)
		tmp3[i]=b[i];
	reverse(tmp2,tmp2+N+1);
	reverse(tmp3,tmp3+M+1);
	int limit=1;
	while(limit<2*(N-M+1))
		limit<<=1;
	for(int i=N-M+1;i<limit;i++)
		tmp2[i]=0;
	for(int i=N-M+1;i<limit;i++)
		tmp3[i]=0;
	Inverse(tmp3,d,limit);
	for(int i=N-M+1;i<limit;i++)
		d[i]=0;
	for(int i=0;i<limit;i++)
		r[i]=(r[i>>1]>>1)|(i&1?(limit>>1):0);
	NTT(tmp2,limit,1);
	NTT(d,limit,1);
	for(int i=0;i<limit;i++)
		d[i]=1ll*d[i]*tmp2[i]%mod;
	NTT(d,limit,-1);
	for(int i=N-M+1;i<limit;i++)
		d[i]=0;
	reverse(d,d+N-M+1);
	return;
}
void Mod(int *a,int N,int *b,int M,int *d,int &n){
	if(N<M){
		n=N;
		for(int i=0;i<=n;i++)
			d[i]=a[i];
		return;
	}
	int limit=1;
	while(limit<=N)
		limit<<=1;
	Divide(a,N,b,M,tmp4);
	for(int i=0;i<=M;i++)
		tmp6[i]=b[i];
	for(int i=M+1;i<limit;i++)
		tmp6[i]=0;
	for(int i=N-M+1;i<limit;i++)
		tmp4[i]=0;
	for(int i=0;i<limit;i++)
		r[i]=(r[i>>1]>>1)|(i&1?(limit>>1):0);
	NTT(tmp4,limit,1);
	NTT(tmp6,limit,1);
	for(int i=0;i<limit;i++)
		tmp6[i]=1ll*tmp6[i]*tmp4[i]%mod;
	NTT(tmp6,limit,-1);
	n=M-1;
	for(int i=0;i<=n;i++)
		d[i]=Minus(a[i],tmp6[i]);
	while(!d[n]&&n)
		n--;
	return;
}

多项式开根

已知多项式 A ( x ) A(x) A(x),求一个多项式 B ( x ) B(x) B(x),满足 A ( x ) ≡ B 2 ( x ) ( m o d x n ) A(x)\equiv B^2(x)\pmod{x^n} A(x)B2(x)(modxn)
我们先考虑n=1的情况,此时直接将多项式的常数项开根即可。(这里需要用到二次剩余的知识,我也不知道怎么做。但有时候常数项确定的时候,我们可以直接赋值)
假设我们已经求出了满足 A ( x ) ≡ B 2 ( x ) ( m o d x n 2 ) A(x)\equiv B^2(x)\pmod{x^{\frac{n}{2}}} A(x)B2(x)(modx2n)的B(x)。
移项得: A ( x ) − B 2 ( x ) ≡ 0 ( m o d x n 2 ) A(x)-B^2(x)\equiv0\pmod{x^{\frac{n}{2}}} A(x)B2(x)0(modx2n)
两边同时平方得: A 2 ( x ) − 2 A ( x ) B 2 ( x ) + B 4 ( x ) ≡ 0 ( m o d x n ) A^2(x)-2A(x)B^2(x)+B^4(x)\equiv0\pmod{x^n} A2(x)2A(x)B2(x)+B4(x)0(modxn)
两边同时加 4 A ( x ) B 2 ( x ) 4A(x)B^2(x) 4A(x)B2(x)得: A 2 ( x ) + 2 A ( x ) B 2 ( x ) + B 4 ( x ) ≡ 4 A ( x ) B 2 ( x ) ( m o d x n ) A^2(x)+2A(x)B^2(x)+B^4(x)\equiv4A(x)B^2(x)\pmod{x^n} A2(x)+2A(x)B2(x)+B4(x)4A(x)B2(x)(modxn)
两边同时除以 4 B 2 ( x ) 4B^2(x) 4B2(x),左边配方得: ( B 2 ( x ) + A ( x ) 2 B ( x ) ) 2 ≡ A ( x ) ( m o d x n ) (\frac{B^2(x)+A(x)}{2B(x)})^2\equiv A(x)\pmod{x^n} (2B(x)B2(x)+A(x))2A(x)(modxn)
再变形可以得到: ( 1 2 B ( x ) + A ( x ) B ( x ) ) 2 ≡ A ( x ) ( m o d x n ) (\frac12B(x)+\frac{A(x)}{B(x)})^2\equiv A(x)\pmod{x^n} (21B(x)+B(x)A(x))2A(x)(modxn)
这个时候,通过NTT,多项式求逆求出 ( 1 2 B ( x ) + A ( x ) B ( x ) ) 2 (\frac12B(x)+\frac{A(x)}{B(x)})^2 (21B(x)+B(x)A(x))2,就可以得到满足 A ( x ) ≡ B 2 ( x ) ( m o d x n ) A(x)\equiv B^2(x)\pmod{x^n} A(x)B2(x)(modxn) B ( x ) B(x) B(x)了。
代码:

void Sqrt(int *a,int *b,int n){
    if(n==1){
        b[0]=1;
        return;
    }
    Sqrt(a,b,n>>1);
    for(register int i=0;i<n;i++)
        d[i]=0;
    inv(b,d,n);
    for(register int i=0;i<(n<<1);i++)
        r[i]=(r[i>>1]>>1)|(i&1?n:0);
    for(register int i=0;i<n;i++)
        c[i]=a[i];
    for(register int i=n;i<(n<<1);i++)
        c[i]=0;
    NTT(d,n<<1,1);
    NTT(c,n<<1,1);
    for(register int i=0;i<(n<<1);i++)
        d[i]=1ll*c[i]*d[i]%mod;
    NTT(d,limit,-1);
    for(register int i=0;i<n;i++)
        b[i]=1ll*(b[i]+d[i])*inv2%mod;
    for(register int i=n;i<(n<<1);i++)
        b[i]=0;
    return;
}

多项式求导

如果多项式 A ( x ) = ∑ i = 0 n a i x i A(x)=\sum_{i=0}^na_ix^i A(x)=i=0naixi,那么多项式 A ( x ) A(x) A(x)的导数就是 A ′ ( x ) = ∑ i = 0 n ( i + 1 ) a i + 1 x i A'(x)=\sum_{i=0}^n(i+1)a_{i+1}x_i A(x)=i=0n(i+1)ai+1xi
怎么算出来的?
把多项式的每一项分别求导就好了。
代码:

void Derivative(int *a,int *b,int n){
    for(register int i=0;i<n;i++)
        b[i]=1ll*a[i+1]*(i+1)%mod;
    b[n]=0;
    return;
}

多项式积分

如果多项式 A ( x ) = ∑ i = 1 n a i x i A(x)=\sum_{i=1}^na_ix^i A(x)=i=1naixi,那么这个多项式的积分 ∫ A ( x ) d x = ∑ i = 1 n 1 i a i − 1 x i \int A(x)dx=\sum_{i=1}^n\frac{1}{i}a_{i-1}x^i A(x)dx=i=1ni1ai1xi
大概就是把求导的过程反过来就行了。
代码:

void Integrate(int *a,int *b,int n){
    for(register int i=n-1;i;i--)
        b[i]=1ll*a[i-1]*inv[i]%mod;
    b[0]=0;
    return;
}

多项式求ln

我们已知多项式 A ( x ) A(x) A(x),要求一个多项式 B ( x ) B(x) B(x)满足 l n A ( x ) = B ( x ) lnA(x)=B(x) lnA(x)=B(x)
我们把 l n A ( x ) lnA(x) lnA(x)求导得到 A ′ ( x ) A ( x ) \frac{A'(x)}{A(x)} A(x)A(x),又因为 l n A ( x ) = B ( x ) lnA(x)=B(x) lnA(x)=B(x),所以 B ( x ) = ∫ A ′ ( x ) A ( x ) B(x)=\int\frac{A'(x)}{A(x)} B(x)=A(x)A(x),于是就可以用多项式求逆+多项式求导+多项式积分搞定啦。
代码:

void Ln(int *a,int *b,int n){
    Derivative(a,b,n);
    for(int i=0;i<(n<<1);i++)
        tmp2[i]=0;
    Inverse(a,tmp2,n);
    for(int i=0;i<(n<<1);i++)
        r[i]=(r[i>>1]>>1)|(i&1?n:0);
    NTT(b,n<<1,1);
    NTT(tmp2,n<<1,1);
    for(int i=0;i<(n<<1);i++)
        b[i]=1ll*b[i]*tmp2[i]%mod;
    NTT(b,n<<1,-1);
    Integrate(b,b,n<<1);
    return;
}

留个坑,剩下的暂时没有心情写,等心情好了再填坑。

多项式exp

多项式求幂

多点求值

快速插值

这些模板先放上来:
e x p exp exp&求幂

void Exp(int *a,int *b,int n){
    if(n==1){
        b[0]=1;
        return;
    }
    Exp(a,b,n>>1);
    for(register int i=0;i<(n<<1);i++)
        tmp3[i]=0;
    Ln(b,tmp3,n);
    for(register int i=0;i<n;i++)
        tmp3[i]=Minus(a[i],tmp3[i]);
    tmp3[0]=Add(tmp3[0],1);
    for(register int i=n;i<(n<<1);i++)
        b[i]=tmp3[i]=0;
    for(register int i=0;i<(n<<1);i++)
        r[i]=(r[i>>1]>>1)|(i&1?n:0);
    NTT(b,n<<1,1);
    NTT(tmp3,n<<1,1);
    for(register int i=0;i<(n<<1);i++)
        b[i]=1ll*b[i]*tmp3[i]%mod;
    NTT(b,n<<1,-1);
    for(register int i=n;i<(n<<1);i++)
        b[i]=0;
    return;
}
void Pow(int *a,int *b,int n,int k){
    Ln(a,tmp4,n);
    for(register int i=0;i<n;i++)
        tmp4[i]=1ll*tmp4[i]*k%mod;
    Exp(tmp4,b,n);
    return;
}

多点求值&快速插值

#include<cstdio>
#include<algorithm>
using namespace std;
const int mod=998244353,g=3,gi=332748118;
int n,x[50010],y[50010],*ans[200010],*a[200010],*b[200010],*c[200010],r[200010],val[200010],a0[200010],a1[200010],b0[200010],b1[200010],tmp1[200010],tmp2[200010],tmp3[200010],tmp4[200010],tmp5[200010],tmp6[200010];
char buffer[1000010],*head,*tail;
char Getchar(){
	if(head==tail){
		int len=fread(buffer,1,1000000,stdin);
		head=buffer,tail=head+len;
		if(head==tail)
			return EOF;
	}
	return *head++;
}
int rd(){
	int x=0;
	char c;
	do c=Getchar();
	while(!isdigit(c));
	do{
		x=(x<<1)+(x<<3)+(c^48);
		c=Getchar();
	}while(isdigit(c));
	return x;
}
int Add(int a,int b){
	return a+b>=mod?a+b-mod:a+b;
}
int Minus(int a,int b){
	return a<b?a-b+mod:a-b;
}
int qpow(int x,int n){
	int ret=1;
	while(n){
		if(n&1)
			ret=1ll*ret*x%mod;
		x=1ll*x*x%mod;
		n>>=1;
	}
	return ret;
}
void NTT(int *a,int limit,int type){
	for(int i=0;i<limit;i++)
		if(i<r[i])
			swap(a[i],a[r[i]]);
	for(int mid=1;mid<limit;mid<<=1){
		int wn=qpow(type==1?g:gi,(mod-1)/(mid<<1));
		for(int i=0;i<limit;i+=(mid<<1)){
			int w=1;
			for(int j=0;j<mid;j++,w=1ll*w*wn%mod){
				int x=a[i+j],y=1ll*w*a[i+j+mid]%mod;
				a[i+j]=Add(x,y);
				a[i+j+mid]=Minus(x,y);
			}
		}
	}
	if(type==-1){
		int inv=qpow(limit,mod-2);
		for(int i=0;i<limit;i++)
			a[i]=1ll*a[i]*inv%mod;
	}
	return;
}
void Inverse(int *a,int *b,int N){
	if(N==1){
		b[0]=qpow(a[0],mod-2);
		return;
	}
	Inverse(a,b,N>>1);
	for(register int i=0;i<N;i++)
		tmp1[i]=a[i];
	for(register int i=N;i<(N<<1);i++)
		tmp1[i]=b[i]=0;
	for(register int i=0;i<(N<<1);i++)
		r[i]=(r[i>>1]>>1)|(i&1?N:0);
	NTT(b,N<<1,1);
	NTT(tmp1,N<<1,1);
	for(register int i=0;i<(N<<1);i++)
		b[i]=Minus(Add(b[i],b[i]),1ll*tmp1[i]*b[i]%mod*b[i]%mod);
	NTT(b,N<<1,-1);
	for(register int i=N;i<(N<<1);i++)
		b[i]=0;
	return;
}
void Divide(int *a,int N,int *b,int M,int *d){
	for(int i=0;i<=N;i++)
		tmp2[i]=a[i];
	for(int i=0;i<=M;i++)
		tmp3[i]=b[i];
	reverse(tmp2,tmp2+N+1);
	reverse(tmp3,tmp3+M+1);
	int limit=1;
	while(limit<2*(N-M+1))
		limit<<=1;
	for(int i=N-M+1;i<limit;i++)
		tmp2[i]=0;
	for(int i=N-M+1;i<limit;i++)
		tmp3[i]=0;
	Inverse(tmp3,d,limit);
	for(int i=N-M+1;i<limit;i++)
		d[i]=0;
	for(int i=0;i<limit;i++)
		r[i]=(r[i>>1]>>1)|(i&1?(limit>>1):0);
	NTT(tmp2,limit,1);
	NTT(d,limit,1);
	for(int i=0;i<limit;i++)
		d[i]=1ll*d[i]*tmp2[i]%mod;
	NTT(d,limit,-1);
	for(int i=N-M+1;i<limit;i++)
		d[i]=0;
	reverse(d,d+N-M+1);
	return;
}
void Mod(int *a,int N,int *b,int M,int *d,int &n){
	if(N<M){
		n=N;
		for(int i=0;i<=n;i++)
			d[i]=a[i];
		return;
	}
	int limit=1;
	while(limit<=N)
		limit<<=1;
	Divide(a,N,b,M,tmp4);
	for(int i=0;i<=M;i++)
		tmp6[i]=b[i];
	for(int i=M+1;i<limit;i++)
		tmp6[i]=0;
	for(int i=N-M+1;i<limit;i++)
		tmp4[i]=0;
	for(int i=0;i<limit;i++)
		r[i]=(r[i>>1]>>1)|(i&1?(limit>>1):0);
	NTT(tmp4,limit,1);
	NTT(tmp6,limit,1);
	for(int i=0;i<limit;i++)
		tmp6[i]=1ll*tmp6[i]*tmp4[i]%mod;
	NTT(tmp6,limit,-1);
	n=M-1;
	for(int i=0;i<=n;i++)
		d[i]=Minus(a[i],tmp6[i]);
	while(!d[n]&&n)
		n--;
	return;
}
void Mul(int *a,int lena,int *b,int lenb,int *c){
	int limit=1;
	while(limit<=lena+lenb)
		limit<<=1;
	for(int i=0;i<=lena;i++)
		tmp1[i]=a[i];
	for(int i=lena+1;i<limit;i++)
		tmp1[i]=0;
	for(int i=0;i<=lenb;i++)
		tmp2[i]=b[i];
	for(int i=lenb+1;i<limit;i++)
		tmp2[i]=0;
	for(int i=0;i<limit;i++)
		r[i]=(r[i>>1]>>1)|(i&1?(limit>>1):0);
	NTT(tmp1,limit,1);
	NTT(tmp2,limit,1);
	for(int i=0;i<limit;i++)
		tmp2[i]=1ll*tmp2[i]*tmp1[i]%mod;
	NTT(tmp2,limit,-1);
	for(int i=0;i<=lena+lenb;i++)
		c[i]=tmp2[i];
	return;
}
void Derivative(int *a,int *b,int N){
	for(int i=0;i<N-1;i++)
		b[i]=1ll*a[i+1]*(i+1)%mod;
	b[N-1]=0;
	return;
}
void solve1(int l,int r,int o){
	if(l==r){
		a[o]=new int[2];
		a[o][0]=x[l]?mod-x[l]:x[l];
		a[o][1]=1;
		return;
	}
	int mid=(l+r)>>1;
	solve1(l,mid,o<<1);
	solve1(mid+1,r,o<<1|1);
	a[o]=new int[r-l+2];
	Mul(a[o<<1],mid-l+1,a[o<<1|1],r-mid,a[o]);
	return;
}
void solve2(int *b,int n,int l,int r,int o){
	int *now=new int[r-l+1];
	Mod(b,n,a[o],r-l+1,now,n);
	if(l==r){
		val[l]=now[0];
		return;
	}
	int mid=(l+r)>>1;
	solve2(now,n,l,mid,o<<1);
	solve2(now,n,mid+1,r,o<<1|1);
	return;
}
void solve3(int l,int r,int o){
	ans[o]=new int[r-l+1];
	if(l==r){
		ans[o][0]=y[l];
		return;
	}
	int mid=(l+r)>>1;
	solve3(l,mid,o<<1);
	solve3(mid+1,r,o<<1|1);
	Mul(ans[o<<1],mid-l,a[o<<1|1],r-mid,tmp4);
	Mul(a[o<<1],mid-l+1,ans[o<<1|1],r-mid-1,tmp3);
	for(int i=0;i<r-l+1;i++)
		ans[o][i]=Add(tmp4[i],tmp3[i]);
	return;
}
int main(){
	n=rd();
	for(int i=0;i<=n;i++)
		x[i]=rd(),y[i]=rd();
	solve1(0,n,1);
	Derivative(a[1],tmp5,n+2);
	solve2(tmp5,n,0,n,1);
	for(int i=0;i<=n;i++)
		y[i]=1ll*y[i]*qpow(val[i],mod-2)%mod;
	solve3(0,n,1);
	for(int i=0;i<=n;i++)
		printf("%d ",ans[1][i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值