洛谷4726 多项式指数函数 学习笔记

题目链接

题意:
给出 n − 1 n-1 n1次多项式 A ( x ) A(x) A(x),求一个 &VeryThinSpace; m o d &VeryThinSpace; &MediumSpace; x n \bmod{\:x^n} modxn下的多项式 B ( x ) B(x) B(x),满足 B ( x ) ≡ e A ( x ) B(x) \equiv e^{A(x)} B(x)eA(x),系数对 999244353 999244353 999244353取模。 n &lt; = 100000 n&lt;=100000 n<=100000

题解:
一个前置知识特别多的题,要用到多项式对数函数、多项式求逆、NTT、多项式牛顿迭代、泰勒展开。
f ( x ) = e A ( x ) ( m o d   x n ) f(x)=e^{A(x)}(mod\ x^n) f(x)=eA(x)(mod xn),那么两边同时取对数得 ln ⁡ f ( x ) = A ( x ) \ln f(x)=A(x) lnf(x)=A(x)。我们设 g ( x ) = ln ⁡ f ( x ) − A ( x ) = 0 g(x)=\ln f(x)-A(x)=0 g(x)=lnf(x)A(x)=0 n = 1 n=1 n=1时,答案可以直接求出。我们假设已经求出了 f 0 ( x ) = e A ( x ) ( m o d   x ⌈ n 2 ⌉ ) f_0(x)=e^{A(x)}(mod\ x^{\lceil\frac{n}{2}\rceil}) f0(x)=eA(x)(mod x2n),那么考虑如何扩展到 f ( x ) = e A ( x ) ( m o d   x n ) f(x)=e^{A(x)}(mod\ x^n) f(x)=eA(x)(mod xn)。我们把 g ( f ( x ) ) g(f(x)) g(f(x)) f 0 ( x ) f_0(x) f0(x)处泰勒展开,得 g ( f ( x ) ) = g ( f 0 ( x ) ) + g ′ ( f 0 ( x ) ) 1 ! ( f ( x ) − f 0 ( x ) ) + g ′ ′ ( f 0 ( x ) ) 2 ! ( f ( x ) − f 0 ( x ) ) 2 + . . . g(f(x))=g(f_0(x))+\frac{g&#x27;(f_0(x))}{1!}(f(x)-f_0(x))+\frac{g&#x27;&#x27;(f_0(x))}{2!}(f(x)-f_0(x))^2+... g(f(x))=g(f0(x))+1!g(f0(x))(f(x)f0(x))+2!g(f0(x))(f(x)f0(x))2+...因为 ln ⁡ f ( x ) \ln f(x) lnf(x)在模 x n x^n xn意义下与 A ( x ) A(x) A(x)相同, x n x^n xn x ⌈ n 2 ⌉ x^{\lceil\frac{n}{2}\rceil} x2n的倍数,所以在模 x ⌈ n 2 ⌉ x^{\lceil\frac{n}{2}\rceil} x2n意义下也与 A ( x ) A(x) A(x)相同,所以在模 x ⌈ n 2 ⌉ x^{\lceil\frac{n}{2}\rceil} x2n意义下 ln ⁡ f ( x ) \ln f(x) lnf(x) ln ⁡ f 0 ( x ) \ln f_0(x) lnf0(x)相等,进一步可以推出在模 x ⌈ n 2 ⌉ x^{\lceil\frac{n}{2}\rceil} x2n意义下 f ( x ) f(x) f(x) f 0 ( x ) f_0(x) f0(x)相等,都是 0 0 0。于是就有 ( f ( x ) − f 0 ( x ) ) 2 = 0 ( m o d   x n ) (f(x)-f_0(x))^2=0(mod\ x^n) (f(x)f0(x))2=0(mod xn),原因是原来前 ⌈ n 2 ⌉ \lceil\frac{n}{2}\rceil 2n次项在模 x ⌈ n 2 ⌉ x^{\lceil\frac{n}{2}\rceil} x2n意义下是 0 0 0,所以平方后前 n n n次项的系数都是 0 0 0,由此可得 ( f ( x ) − f 0 ( x ) ) m (f(x)-f_0(x))^m (f(x)f0(x))m中所有次数 m m m大于等于2的项在模 x n x^n xn意义下都是 0 0 0了,于是我们只需要保留前两项即可,也就是 g ( f ( x ) ) ≡ g ( f 0 ( x ) ) + g ′ ( f 0 ( x ) ) ( f ( x ) − f 0 ( x ) ) ( m o d   x n ) g(f(x))\equiv g(f_0(x))+g&#x27;(f_0(x))(f(x)-f_0(x))(mod\ x^n) g(f(x))g(f0(x))+g(f0(x))(f(x)f0(x))(mod xn)。化简一下就是 f ( x ) = f 0 ( x ) − g ( f 0 ( x ) ) g ′ ( f 0 ( x ) ) ( m o d   x n ) f(x)=f_0(x)-\frac{g(f_0(x))}{g&#x27;(f_0(x))}(mod\ x^n) f(x)=f0(x)g(f0(x))g(f0(x))(mod xn) 再利用一下复合函数的求导公式可得: f ( x ) = f 0 ( x ) ( 1 − ln ⁡ f 0 ( x ) + A ( x ) ) ( m o d   x n ) f(x)=f_0(x)(1−\ln f_0(x)+A(x))(mod\ x^n) f(x)=f0(x)(1lnf0(x)+A(x))(mod xn)

这个东西要用多项式牛顿迭代。具体实现的话就是用一种倍增的方法,复杂度证明和多项式求逆差不多。式子中的 ln ⁡ \ln ln用多项式对数函数就好,推导就到这里了。

代码:

#include <bits/stdc++.h>
using namespace std;

int n,m,l,rev[800010];
long long a[800010],b[800010],xx[800010],yy[800010],zz[800010],c[800010],d[800010];
const long long mod=998244353,g=3,gi=332748118;
inline int read()
{
	int x=0;
	char s=getchar();
	while(s>'9'||s<'0')
	s=getchar();
	while(s>='0'&&s<='9')
	{
		x=x*10+s-'0';
		s=getchar();
	}
	return x;
}
inline long long ksm(long long x,long long y)
{
	long long res=1;
	while(y)
	{
		if(y&1)
		res=res*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return res;
}
inline void ntt(long long *a,int dft,int len)
{
	l=0;
	m=len;
	for(len=1;len<m;len<<=1)
	++l;
	for(int i=0;i<len;++i)
	rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
	for(int i=0;i<len;++i)
	{
		if(i<rev[i])
		swap(a[i],a[rev[i]]);
	}
	for(int i=1;i<len;i<<=1)
	{
		long long wn=ksm((dft==1?g:gi),(mod-1)/(i<<1));
		for(int j=0,p=(i<<1);j<len;j+=p)
		{
			long long w=1;
			for(int k=0;k<i;++k)
			{
				long long x=a[j+k],y=w*a[i+j+k]%mod;
				a[j+k]=(x+y)%mod;
				a[i+j+k]=(x-y+mod)%mod;
				w=w*wn%mod;
			}
		}
	}
	if(dft==-1)
	{
		long long ni=ksm(m,mod-2);
		for(int i=0;i<len;++i)
		a[i]=a[i]*ni%mod;
	}
}
inline void inv(long long *a,long long *b,int len)
{
	if(len==1)
	{
		b[0]=ksm(a[0],mod-2);
		return;
	}
	inv(a,b,len>>1);
	for(int i=0;i<len;++i)
	{
		xx[i]=a[i];
		yy[i]=b[i];
	}
	ntt(xx,1,len<<1);
	ntt(yy,1,len<<1);
	for(int i=0;i<(len<<1);++i)
	xx[i]=xx[i]*yy[i]%mod*yy[i]%mod;
	ntt(xx,-1,len<<1);
	for(int i=0;i<len;++i)
	b[i]=(b[i]*2-xx[i]+mod)%mod;
	for(int i=0;i<(len<<1);++i)
	{
		xx[i]=0;
		yy[i]=0;
	}
}
inline void ln(long long *a,long long *b,int len)
{
	for(int i=1;i<len;++i)
	d[i-1]=a[i]*i%mod;
	inv(a,zz,len);
	ntt(d,1,len<<1);
	ntt(zz,1,len<<1);
	for(int i=0;i<(len<<1);++i)
	d[i]=d[i]*zz[i];
	ntt(d,-1,len<<1);
	for(int i=1;i<len;++i)
	b[i]=d[i-1]*ksm(i,mod-2)%mod;
	b[0]=0;
	for(int i=0;i<(len<<1);++i)
	{
		d[i]=0;
		zz[i]=0;
	}
}
/*inline void exp(long long *a,long long *b,int len)
{
	if(len==1)
	{
		b[0]=1;
		return;
	}
	exp(a,b,len>>1);
	ln(b,yy,len);
	for(int i=0;i<n;++i)
	xx[i]=a[i];
	ntt(xx,1,len<<1);
	ntt(yy,1,len<<1);
	ntt(b,1,len<<1);
	for(int i=0;i<(len<<1);++i)
	b[i]=b[i]*(1-yy[i]+xx[i]+mod)%mod;
	ntt(b,-1,len<<1);
	for(int i=n;i<(len<<1);++i)
	b[i]=0;
	for(int i=0;i<(len<<1);++i)
	{
		xx[i]=0;
		yy[i]=0;
	}
}*/
inline void exp(long long *a,long long *b,int len)
{
	if(len==1)
	{
		b[0]=1;
		return;
	}
	exp(a,b,len>>1);
	ln(b,yy,len);
	for(int i=0;i<len;++i)
	xx[i]=a[i];
	xx[0]++;
	for(int i=0;i<len;++i)
	yy[i]=(xx[i]-yy[i]+mod)%mod;
	ntt(yy,1,len<<1);
	ntt(b,1,len<<1);
	for(int i=0;i<(len<<1);++i)
	b[i]=b[i]*yy[i]%mod;
	ntt(b,-1,len<<1);
	for(int i=n;i<(len<<1);++i)
	b[i]=0;
	for(int i=0;i<(len<<1);++i)
	{
		xx[i]=0;
		yy[i]=0;
	}
} 
int main()
{
	scanf("%d",&n);
	for(int i=0;i<=n-1;++i)
	a[i]=read();
	int ji;
	for(ji=1;ji<=n;ji<<=1);
	exp(a,c,ji);
	for(int i=0;i<n;++i)
	printf("%lld ",c[i]);
	printf("\n"); 
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值