[JZOJ5529] 朱老师的难题 【多项式】【生成函数】

Description

有一个n个数的严格递增序列a
定义一组数为序列a的一个非空子集,也就是说这里面的元素不能重复,也没有顺序之分。

一组数 S S S的权值为 ( − 1 ) ∣ S ∣ + 1 ∏ i ∈ S a i (-1)^{|S|+1}\prod\limits_{i\in S} a_i (1)S+1iSai

你可以选择若干组数(组与组之间有顺序,一个元素可以同时出现在很多组)作为一个选择方案,这个方案的权值是所有选择的组的权值的乘积

定义函数 f ( m ) f(m) f(m)为所有满足选的总题数为m的方案的权值和

可以证明(原题这么说的),存在n个数对 ( b i , c i ) (b_i,c_i) (bi,ci),满足对于任意的m,都有 f ( m ) = ∑ i = 1 n b i c i m f(m)=\sum\limits_{i=1}^{n}b_ic_i^m f(m)=i=1nbicim

c i c_i ci从小到大排序后,可以证明数对是唯一的
求出这些数对。
n ≤ 100000 n\leq 100000 n100000

Solution

咋一看无从下手…
感觉它是一道多项式题。

考虑用生成函数来解决
设一组数的一般生成函数为 G ( x ) G(x) G(x), 指数是选的数的数量,系数是权值
P ( x ) = ∏ i = 1 n ( 1 − a i x ) P(x)=\prod\limits_{i=1}^{n}(1-a_ix) P(x)=i=1n(1aix)
容易得到 G ( x ) = 1 − P ( x ) G(x)=1-P(x) G(x)=1P(x)

f ( m ) f(m) f(m)的生成函数为 F ( x ) F(x) F(x)
枚举选多少组,也容易得到 F ( x ) = ∑ i > 0 G i ( x ) = 1 1 − G ( x ) = 1 P ( x ) F(x)=\sum\limits_{i>0}G^i(x)={1\over 1-G(x)}={1\over P(x)} F(x)=i>0Gi(x)=1G(x)1=P(x)1

同时又有 F ( x ) = ∑ i ≥ 0 f ( i ) x i = ∑ i ≥ 0 ∑ j = 1 n b j c j i x i F(x)=\sum\limits_{i\geq 0} f(i)x^i=\sum\limits_{i\geq 0}\sum\limits_{j=1}^{n}b_jc_j^ix^i F(x)=i0f(i)xi=i0j=1nbjcjixi

交换主体
F ( x ) = ∑ i ≥ 0 f ( i ) x i = ∑ j = 1 n b j ∑ i ≥ 0 c j i x i = ∑ j = 1 n b j 1 − c j x F(x)=\sum\limits_{i\geq 0} f(i)x^i=\sum\limits_{j=1}^{n}b_j\sum\limits_{i\geq 0}c_j^ix^i=\sum\limits_{j=1}^{n}{b_j\over 1-c_jx} F(x)=i0f(i)xi=j=1nbji0cjixi=j=1n1cjxbj

通分,有
F ( x ) = 1 P ( x ) = ∑ j = 1 n b j ∏ k ̸ = j ( 1 − c k x ) ∏ k ( 1 − c k x ) F(x)={1\over P(x)}={\sum\limits_{j=1}^{n}b_j\prod\limits_{k\not =j}(1-c_kx)\over \prod\limits_{k}(1-c_kx)} F(x)=P(x)1=k(1ckx)j=1nbjk̸=j(1ckx)

此处可以感受一下,序列c和序列a是完全相同的
为什么呢?
这里有个不太靠谱的证明

考虑反证,假设存在 i , a i ̸ = c i i,a_i\not= c_i i,ai̸=ci
由于这里的生成函数都是形式幂级数,我们不妨令 x = 1 a i x={1\over a_i} x=ai1

那么 P ( x ) = 0 P(x)=0 P(x)=0,由于a互不相同,右边分母总是至少有一项是不能与左边分母约掉的,这样就矛盾了
因此 c i = a i c_i=a_i ci=ai

或者我们用构造的思想,令a与c等价,那么我们总是能够构造出一组解

那么移项,就有 1 = ∑ j = 1 n b j ∏ k ̸ = j ( 1 − a k x ) 1=\sum\limits_{j=1}^{n}b_j\prod\limits_{k\not =j}(1-a_kx) 1=j=1nbjk̸=j(1akx)

我们想要把某个bj分离出来

x = 1 a j x={1\over a_j} x=aj1,那么除了 b j b_j bj这一个,其他的全部都是0

就有 1 = b j ∏ k ̸ = j ( 1 − a k a j ) 1=b_j\prod\limits_{k\not =j}\left(1-{a_k\over a_j}\right) 1=bjk̸=j(1ajak)

移项再化式子,可得 b j = 1 − a j ∗ 1 a j P ( 1 a j ) b_j={1-a_j*{1\over a_j}\over P\left({1\over a_j}\right)} bj=P(aj1)1ajaj1

上下都是0,可以用洛必达法则

b j = lim ⁡ x → 1 a j 1 − a j x P ( x ) = lim ⁡ x → 1 a j − a j P ′ ( x ) = − a j P ′ ( 1 a j ) b_j=\lim\limits_{x\to {1\over a_j}} {1-a_jx\over P(x)}=\lim\limits_{x\to {1\over a_j}} {-a_j\over P'(x)}={-a_j\over P'({1\over a_j})} bj=xaj1limP(x)1ajx=xaj1limP(x)aj=P(aj1)aj

这样我们只需要求出 P ′ ( x ) P'(x) P(x)在每个 1 a j 1\over a_j aj1的点值,带入计算即可
多项式函数求导直接每一项左移乘上系数
剩下的就是经典的多项式多点求值了
套模板即可…

总的时间复杂度是 O ( n log ⁡ 2 n ) O(n\log ^2n) O(nlog2n)

Code

#include 
#include 
#include 
#include 
#include 
#include 
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define M 524288
#define mo 1811939329
#define N 100005
#define LL long long
using namespace std;
LL a[M+1],b[M+1],c[M+1],u1[M+1],wg[M+1],u2[M+1],wi[M+1],r[M+1],q[M+1],cf[20],l2[M+1],ny[M+1],a1[20][M+1],ans[N],s1[20][M+1];
int bit[M+1],n,m,fi[20][N+1],le[20][N+1],lf;
LL ksm(LL k,LL n)
{
	LL s=1;
	for(;n;n>>=1,k=k*k%mo) if(n&1) s=s*k%mo;
	return s;
}
void prp(int num)
{
	fo(i,0,num) wi[i]=wg[i*(M/num)];
	fo(i,0,num-1) bit[i]=(bit[i>>1]>>1)|((i&1)<<(l2[num]-1));
}
void NTT(LL *a,bool pd,int num)
{
	fo(i,0,num-1) if(i<bit[i]) swap(a[i],a[bit[i]]);
	LL w,v;
	for(int lim=num>>1,m=2,half=1;m<=num;half=m,lim>>=1,m<<=1)
	{
		fo(i,0,half-1)
		{
			w=(!pd)?wi[i*lim]:wi[num-i*lim];
			for(int j=i;j<num;j+=m)
			{
				v=w*a[j+half]%mo;
				a[j+half]=(a[j]-v+mo)%mo;
				a[j]=(a[j]+v)%mo;
			}
		}
	}
	if(pd) fo(i,0,num-1) a[i]=a[i]*ny[num]%mo;
}
void make(int l,LL *a,LL *b)
{
	b[0]=ksm(a[0],mo-2);
	for(int m=1,t=2,num=4;m<=l;m=t,t=num,num<<=1)
	{
		prp(num);
		fo(i,0,m-1) c[i]=a[i],u2[i]=b[i];
		fo(i,m,t-1) c[i]=a[i],u2[i]=b[i]=0;
		fo(i,t,num-1) c[i]=u2[i]=b[i]=0;
		NTT(c,0,num),NTT(b,0,num);
		fo(i,0,num-1) b[i]=b[i]*b[i]%mo*c[i]%mo;
		NTT(b,1,num);
		fo(i,0,t-1) b[i]=((LL)2*u2[i]-b[i]+mo)%mo;
		fo(i,t,num-1) b[i]=0;
	}
}
void rev(int num,LL *a,LL *b)
{
	fo(i,0,num-1) b[i]=a[num-i-1];
}
void div(int n,int m,LL *a,LL *b,LL *d,LL *r)
{
	rev(m,b,r);
	fo(i,m,n-m+1) r[i]=0;
	make(n-m+1,r,d);
	int num=cf[l2[n]]*2;
	prp(num),rev(n,a,u2);
	fo(i,n-m+1,num-1) d[i]=u2[i]=0;
	
	NTT(d,0,num),NTT(u2,0,num);
	fo(i,0,num-1) d[i]=d[i]*u2[i]%mo;
	NTT(d,1,num);
	fo(i,n-m+1,num-1) d[i]=0;
	fo(i,0,(n-m)>>1) swap(d[i],d[n-m-i]);

	num=cf[l2[n]],prp(num);
	fo(i,0,m-1) r[i]=d[i],u2[i]=b[i];
	fo(i,m,num-1) r[i]=d[i],u2[i]=0;
	NTT(r,0,num),NTT(u2,0,num);
	fo(i,0,num-1) r[i]=r[i]*u2[i]%mo;
	NTT(r,1,num);
	fo(i,0,m-1) r[i]=(a[i]-r[i]+mo)%mo;
	fo(i,m,num-1) r[i]=0;
}
void prd(int t,int l,int r)
{
	if(l==r)
	{
		fi[t][l]=lf+1;
		le[t][l]=2;
		a1[t][fi[t][l]+1]=-a[l],a1[t][fi[t][l]]=1;
		lf+=2;
		return;
	}
	int mid=(l+r)>>1;
	prd(t+1,l,mid),prd(t+1,mid+1,r);
}
void prd1(int t,int l,int r)
{
	if(l==r)
	{
		fi[t][l]=lf+1;
		le[t][l]=2;
		a1[t][fi[t][l]]=mo-ksm(a[l],mo-2),a1[t][fi[t][l]+1]=1;
		lf+=2;
		return;
	}
	int mid=(l+r)>>1;
	prd1(t+1,l,mid),prd1(t+1,mid+1,r);
}
void doit(int t,int l,int r)
{
	if(l==r) return;
	int mid=(l+r)>>1;
	doit(t+1,l,mid),doit(t+1,mid+1,r);
	fi[t][l]=fi[t+1][l];
	int num=cf[l2[le[t+1][l]+le[t+1][mid+1]-1]];
	prp(num);
	fo(i,0,num-1)
	{
		u1[i]=(i<le[t+1][l])?a1[t+1][fi[t+1][l]+i]:0;
		u2[i]=(i<le[t+1][mid+1])?a1[t+1][fi[t+1][mid+1]+i]:0;
	}
	NTT(u1,0,num),NTT(u2,0,num);
	fo(i,0,num-1) u1[i]=u1[i]*u2[i]%mo;
	NTT(u1,1,num);
	le[t][l]=le[t+1][l]+le[t+1][mid+1]-1;
	fo(i,0,le[t][l]) a1[t][fi[t][l]+i]=u1[i];
}
void query(int t,int l,int r,int n)
{
	if(l==r) ans[l]=s1[t][0];
	else
	{
		int mid=(l+r)>>1;
		div(n,le[t+1][l],s1[t],a1[t+1]+fi[t+1][l],q,s1[t+1]);
		query(t+1,l,mid,le[t+1][l]-1);
		div(n,le[t+1][mid+1],s1[t],a1[t+1]+fi[t+1][mid+1],q,s1[t+1]);
		query(t+1,mid+1,r,le[t+1][mid+1]-1);
	}
}
int main()
{
	cf[0]=1;
	fo(i,1,19) cf[i]=cf[i-1]<<1,l2[cf[i]]=i;
	fod(i,M-1,1) if(!l2[i]) l2[i]=l2[i+1];
	ny[1]=1;
	fo(i,2,M) ny[i]=(-ny[mo%i]*(LL)(mo/i)%mo+mo)%mo;
	wg[0]=1;
	LL c=ksm(13,(mo-1)/M);
	fo(i,1,M) wg[i]=wg[i-1]*c%mo;
	cin>>n;
	fo(i,0,n-1) scanf("%lld",&a[i]);
	prd(0,0,n-1);
	doit(0,0,n-1);
	fo(i,fi[0][0],fi[0][0]+le[0][0]-2) s1[0][i-fi[0][0]]=a1[0][i+1]*(LL)(i-fi[0][0]+1)%mo; 
	memset(fi,0,sizeof(fi));
	memset(le,0,sizeof(le));
	memset(a1,0,sizeof(a1));
	lf=0;
	prd1(0,0,n-1);
	doit(0,0,n-1);
	query(0,0,n-1,le[0][0]-1);
	fo(i,0,n-1) printf("%lld %lld\n",(-a[i]*ksm(ans[i],mo-2)%mo+mo)%mo,a[i]);
}

你可能感兴趣的:(题解,---多项式,————生成函数,————FFT)