[SDOI2017]遗忘的集合

一、题目

点此看题

二、解法

考虑生成函数,最后的方案数其实是若干个母函数闭形式相乘的一个多项式,可列出下式:
F ( x ) = ∏ i = 1 n ( 1 1 − x i ) a i F(x)=\prod_{i=1}^n (\frac{1}{1-x^i})^{a_i} F(x)=i=1n(1xi1)ai先推一波柿子:
ln ⁡ ( F ( x ) ) = ∑ i = 1 n a i ln ⁡ ( 1 1 − x i ) \ln(F(x))=\sum_{i=1}^na_i\ln(\frac{1}{1-x^i}) ln(F(x))=i=1nailn(1xi1) − ln ⁡ ( F ( x ) ) = ∑ i = 1 n a i ln ⁡ ( 1 − x i ) -\ln(F(x))=\sum_{i=1}^n a_i\ln(1-x^i) ln(F(x))=i=1nailn(1xi)上面的变形本质上是把 − 1 -1 1的指数提出来,然后就有了一个负号,有一个结论:
ln ⁡ ( 1 − x i ) = − ∑ j = 1 ∞ x i j j \ln (1-x^i)=-\sum_{j=1}^\infty \frac{x^{ij}}{j} ln(1xi)=j=1jxij下面给出证明,设 A ( x ) = 1 − x i A(x)=1-x^i A(x)=1xi G ( x ) = ln ⁡ ( 1 − x i ) G(x)=\ln(1-x^i) G(x)=ln(1xi)
ln ⁡ ( A ( x ) ) = G ( x ) \ln(A(x))=G(x) ln(A(x))=G(x) A ′ ( x ) A ( x ) = G ′ ( x ) \frac{A'(x)}{A(x)}=G'(x) A(x)A(x)=G(x) − i x i − 1 1 − x i = G ′ ( x ) \frac{-ix^{i-1}}{1-x^i}=G'(x) 1xiixi1=G(x)发现上式像一个闭形式,我们可以把分子提到前面去,然后转化成母函数:
− ∑ j = 0 ∞ i x i − 1 + i j = G ′ ( x ) -\sum_{j=0}^\infty ix^{i-1+ij}=G'(x) j=0ixi1+ij=G(x) − ∑ j = 0 ∞ i x i + i j i + i j = G ( x ) -\sum_{j=0}^\infty \frac{ix^{i+ij}}{i+ij}=G(x) j=0i+ijixi+ij=G(x)然后把所有的 j j j右移一次,柿子就更加简洁:
− ∑ j = 1 ∞ x i j j = G ( x ) = ln ⁡ ( 1 − x i ) -\sum_{j=1}^\infty \frac{x^{ij}}{j}=G(x)=\ln (1-x^i) j=1jxij=G(x)=ln(1xi)得到这个结论后,可以继续推 F ( x ) F(x) F(x)的柿子:
− ln ⁡ ( F ( x ) ) = − ∑ i = 1 n a i ∑ j = 1 ∞ x i j j -\ln(F(x))=-\sum_{i=1}^n a_i\sum_{j=1}^\infty \frac{x^{ij}}{j} ln(F(x))=i=1naij=1jxij我们转而枚举 d = i j d=ij d=ij,柿子就变成了这样:
ln ⁡ ( F ( x ) ) = ∑ d = 1 n x d ∑ i ∣ d a i i d \ln(F(x))=\sum_{d=1}^{n}x^d\sum_{i|d}a_i\frac{i}{d} ln(F(x))=d=1nxdidaidi考虑 a i a_i ai的贡献, a 1 a_1 a1的值我们是确定了的,那么我们就可以把所有系数减去 a 1 a_1 a1的贡献,我们枚举每个数,用类似埃氏筛的方式消去贡献,这样枚举到 i i i时都能得到 a i a_i ai,最开始时需要把每个值乘以 i i i,时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

但是模数并不是适合普通 NTT \text{NTT} NTT的模数,多项式 ln ⁡ \ln ln需要写三模 NTT \text{NTT} NTT,然后我就高兴地被卡了,代码仅供参考,并不能在规定时间内通过所有数据。

#pragma GCC optimize(2)
#include <cstdio>
#include <iostream>
#include <cmath>
#define LL long long
using namespace std;
const int MAXN = 1000005;
LL read()
{
    LL num=0,flag=1;char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
    return num*flag;
}
LL n,lg,cnt,len,a[MAXN],b[MAXN],c[MAXN],Rev[MAXN];
LL ans[MAXN][4],A[MAXN],B[MAXN],C[MAXN],D[MAXN];
int p1=469762049,p2=998244353,p3=1004535809,p;
LL qkpow(LL a,LL b,int mod)
{
	LL res=1;
	while(b>0)
	{
		if(b&1) res=res*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return res;
}
LL exgcd(LL a,LL b,LL &x,LL &y)
{
	if(b==0){x=1;y=0;return a;}
	LL d=exgcd(b,a%b,y,x);
	y-=(a/b)*x;
	return d;
}
void NTT(LL *a,const LL len,LL tmp,int MOD)
{
	for(LL i=0;i<len;i++)
	{
		Rev[i]=(Rev[i>>1]>>1)|((i&1)<<(lg-1));
		if(i<Rev[i])
			swap(a[i],a[Rev[i]]);
	}
	for(LL s=2;s<=len;s<<=1)
	{
		LL t=s/2,w=(tmp==1)?qkpow(3,(MOD-1)/s,MOD):qkpow(3,(MOD-1)-(MOD-1)/s,MOD);
		for(LL i=0;i<len;i+=s)
		{
			LL x=1;
			for(LL j=0;j<t;j++,x=x*w%MOD)
			{
				LL fe=a[i+j],fo=a[i+j+t];
				a[i+j]=(fe+x*fo)%MOD;
				a[i+j+t]=((fe-fo*x)%MOD+MOD)%MOD;
			}
		}
	}
	if(tmp==1) return ;
	LL inv=qkpow(len,MOD-2,MOD);
	for(LL i=0;i<len;i++)
		a[i]=a[i]*inv%MOD;
}
void fuck(LL *a,LL *b,int p,LL id)
{
	len=lg=1;while(len<=2*n) len<<=1,lg++;
	lg--;
	for(LL i=0;i<len;i++) A[i]=B[i]=0;
	for(LL i=0;i<n;i++) A[i]=a[i];
	for(LL i=0;i<n;i++) B[i]=b[i];
	NTT(A,len,1,p);NTT(B,len,1,p);
	for(LL i=0;i<len;i++) A[i]=A[i]*B[i]%p;
	NTT(A,len,-1,p);
	for(LL i=0;i<2*n;i++)
		ans[i][id]=A[i];
}
LL excrt(LL a,LL b)
{
	LL m[3]={0,p1,p2},r[3]={0,a,b};
	LL M=m[1],R=r[1],x=0,y=0;
	for(LL i=2;i<=2;i++)
	{
		LL d=exgcd(M,m[i],x,y);
		if((R-r[i])%d) return -1;
		x=x*(R-r[i])/d%m[i];
		R-=x*M;
		M=M*m[i]/d;
		R%=M;
	}
	return (R%M+M)%M;
}
void mul(LL n,LL *a,LL *b,LL *c)
{
	fuck(a,b,p1,1);
	fuck(a,b,p2,2);
	fuck(a,b,p3,3);
	for(LL i=0;i<2*n;i++)
	{
		LL x=excrt(ans[i][1],ans[i][2]);
		LL y=(ans[i][3]-x)%p3*qkpow(p1,p3-2,p3)%p3*qkpow(p2,p3-2,p3)%p3;
		y=(y%p3+p3)%p3;
		c[i]=((y*p1%p*p2%p+x)%p+p)%p;
	}
}
void work(LL n,LL *a,LL *b)
{
	mul(n,b,b,c);
	mul(n,a,c,c);
	for(LL i=0;i<n;i++)
		b[i]=((2*b[i]-c[i])%p+p)%p;
}
void inv(LL n,LL *a,LL *b)
{
	LL cur=1;
	for(LL i=0;i<n;i++) b[i]=0;
	b[0]=qkpow(a[0],p-2,p);
	while(cur<n)
	{
		cur<<=1;
		work(cur,a,b);
	}
}
void ln(LL n,LL *a,LL *b)
{
	for(LL i=0;i<n;i++)
		C[i]=a[i];
	inv(n,C,D);
	for(LL i=0;i<n;i++)
		C[i]=C[i+1]*(i+1)%p;
	mul(n,C,D,C);
	b[0]=0;
	for(LL i=1;i<n;i++)
		b[i]=C[i-1]*qkpow(i,p-2,p)%p;
}
signed main()
{
	n=read();p=read();
	a[0]=1;
	for(LL i=1;i<=n;i++)
		a[i]=read();
	ln(n+1,a,b);
	for(LL i=1;i<=n;i++)
		b[i]=i*b[i]%p;
	for(LL i=1;i<=n;i++)
		for(LL j=i+i;j<=n;j+=i)
			b[j]=((b[j]-b[i])%p+p)%p;
	for(LL i=1;i<=n;i++)
		if(b[i])
			cnt++;
	printf("%lld\n",cnt);
	for(LL i=1;i<=n;i++)
		if(b[i])
			printf("%lld ",i);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值