loj2271/洛谷P3784/bzoj4913 [SDOI2017]遗忘的集合 生成函数+MTT+多项式求ln

本文深入探讨了组合数学中生成函数的应用,通过详细的数学推导,解释了如何利用生成函数解决集合内数字的组合问题。文章展示了生成函数在求解特定组合数问题中的强大能力,并提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题面分析

对于集合内的数iii,考虑取多少个它,生成函数为∑j=0infxij=11−xi\sum_{j=0}^{inf} x^{ij}=\frac{1}{1-x^i}j=0infxij=1xi1

ti∈[0,1]t_i \in[0,1]ti[0,1]表示集合里是否存在数字iii,则fff对应的生成函数就是:

F(x)=∏i=1n(11−xi)tiF(x)=\prod_{i=1}^{n} (\frac{1}{1-x^i})^{t_i}F(x)=i=1n(1xi1)ti

两边同时取ln(为什么想到取ln?我也不知道啊QAQ)得:

−ln⁡F(x)=∑i=1ntiln⁡(1−xi)-\ln{F(x)}=\sum_{i=1}^{n} t_i \ln{(1-x^i)}lnF(x)=i=1ntiln(1xi)

ln⁡(1−xi)\ln{(1-x^i)}ln(1xi)求导得:

−ixi−11−xi\frac{-ix^{i-1}}{1-x^i}1xiixi1

展开得:(−ixi−1)∑j=0infxji=−∑j=0infixij+i−1(-ix^{i-1})\sum_{j=0}^{inf} x^{ji} = -\sum_{j=0}^{inf} ix^{ij+i-1}(ixi1)j=0infxji=j=0infixij+i1

然后积分得:−∑j=0infixi(j+1)i(j+1)=−∑j=1infxijj-\sum_{j=0}^{inf} \frac{ix^{i(j+1)}}{i(j+1)}=-\sum_{j=1}^{inf} \frac{x^{ij}}{j}j=0infi(j+1)ixi(j+1)=j=1infjxij

ln⁡(1−xi)−∑j=1infxijj\ln{(1-x^i)}-\sum_{j=1}^{inf} \frac{x^{ij}}{j}ln(1xi)j=1infjxij带入原式得:

ln⁡F(x)=∑i=1nti∑j=1infxijj\ln{F(x)}=\sum_{i=1}^{n}t_i \sum_{j=1}^{inf} \frac{x^{ij}}{j}lnF(x)=i=1ntij=1infjxij

T=ijT=ijT=ij,得:

ln⁡F(x)=∑T=1inf(∑d∣TtiiT)xT\ln{F(x)}=\sum_{T=1}^{inf} (\sum_{d|T} t_i \frac{i}{T}) x^TlnF(x)=T=1inf(dTtiTi)xT

F(x)F(x)F(x)求ln后第iii项为gig_igi,则gi=∑d∣TtiiTg_i=\sum_{d|T} t_i \frac{i}{T}gi=dTtiTi

那么求tit_iti只用反演即可,发现没必要用莫比乌斯,用每次求出来的tit_iti去减掉它在iii的倍数项的ggg中的贡献,调和级数复杂度地求tit_iti即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
	int q=0;char ch=' ';
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
	return q;
}
const int N=524300,M=32767;
typedef long long LL;
typedef long double db;
const db pi=acos(-1);
int n,mod,ans;
int rev[N],len[N],F[N],G[N],inv[N];
int k1[N],k2[N],k3[N],k4[N],k5[N];
struct com{db r,i;}a[N],b[N],Aa[N],Ab[N],Ba[N],Bb[N];
com operator + (com A,com B) {return (com){A.r+B.r,A.i+B.i};}
com operator - (com A,com B) {return (com){A.r-B.r,A.i-B.i};}
com operator * (com A,com B) {return (com){A.r*B.r-A.i*B.i,A.r*B.i+A.i*B.r};}
com conj(com A) {return (com){A.r,-A.i};}

int qm(int x) {return x>=mod?x-mod:x;}
int ksm(int x,int y) {
	int re=1;
	for(;y;y>>=1,x=1LL*x*x%mod) if(y&1) re=1LL*re*x%mod;
	return re;
}

void FFT(com *a,int n) {
    for(RI i=0;i<n;++i) if(rev[i]>i) swap(a[i],a[rev[i]]);
    for(RI i=1;i<n;i<<=1) {
        com wn=(com){cos(pi/i),sin(pi/i)};
        for(RI j=0;j<n;j+=(i<<1)) {
            com t1,t2,w=(com){1,0};
            for(RI k=0;k<i;++k,w=w*wn)
                t1=a[j+k],t2=w*a[j+i+k],a[j+k]=t1+t2,a[j+i+k]=t1-t2;
        }
    }
}
void mul(int *ka,int *kb,int *kc,int n) {
    for(RI i=0;i<n;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(len[n]-1));
    for(RI i=0;i<(n>>1);++i) {
        a[i]=(com){(db)(ka[i]&M),(db)(ka[i]>>15)};
        b[i]=(com){(db)(kb[i]&M),(db)(kb[i]>>15)};
        a[i+(n>>1)]=b[i+(n>>1)]=(com){0,0};
    }
    FFT(a,n),FFT(b,n);
    for(RI i=0;i<n;++i) {
        int j=(n-i)&(n-1);
        com kAa=(a[i]+conj(a[j]))*(com){0.5,0};
        com kAb=(a[i]-conj(a[j]))*(com){0,-0.5};
        com kBa=(b[i]+conj(b[j]))*(com){0.5,0};
        com kBb=(b[i]-conj(b[j]))*(com){0,-0.5};
        Aa[j]=kAa*kBa,Ab[j]=kAa*kBb,Ba[j]=kBa*kAb,Bb[j]=kAb*kBb;
    }
    for(RI i=0;i<n;++i)
        a[i]=Aa[i]+Ab[i]*(com){0,1},b[i]=Ba[i]+Bb[i]*(com){0,1};
    FFT(a,n),FFT(b,n);
    for(RI i=0;i<n;++i) {
        int kAa=(LL)(a[i].r/n+0.5)%mod;
        int kAb=(LL)(a[i].i/n+0.5)%mod;
        int kBa=(LL)(b[i].r/n+0.5)%mod;
        int kBb=(LL)(b[i].i/n+0.5)%mod;
        kc[i]=qm(((LL)kAa+((LL)(kAb+kBa)<<15)+((LL)kBb<<30))%mod+mod);
    }
}

void getJF(int *a,int *b,int n)
	{for(RI i=1;i<n;++i) b[i]=1LL*a[i-1]*inv[i]%mod; b[0]=0;}
void getdao(int *a,int *b,int n)
	{for(RI i=1;i<n;++i) b[i-1]=1LL*a[i]*i%mod; b[n-1]=0;}
void getinv(int *a,int *b,int n) {
	if(n==1) {b[0]=ksm(a[0],mod-2);return;}
	getinv(a,b,n>>1),mul(a,b,k3,n<<1),mul(k3,b,k4,n<<1);
	for(RI i=0;i<n;++i) b[i]=qm(qm(b[i]+b[i])-k4[i]+mod),b[i+n]=0;
}
void getln(int *a,int *b,int n)
	{getdao(a,k1,n),getinv(a,k2,n),mul(k1,k2,k5,n<<1),getJF(k5,b,n);}

int main()
{
	n=read(),mod=read();
	for(RI i=1;i<=n;++i) F[i]=read();
	int kn=1;while(kn<=n) kn<<=1,len[kn]=len[kn>>1]+1;
	len[kn<<1]=len[kn]+1;
	inv[1]=1;for(RI i=2;i<(kn<<1);++i) inv[i]=1LL*inv[mod%i]*qm(mod-mod/i)%mod;
	F[0]=1,getln(F,G,kn);
	for(RI i=1;i<=n;++i) G[i]=1LL*G[i]*i%mod;
	for(RI i=1;i<=n;++i)
		for(RI j=i+i;j<=n;j+=i) G[j]=qm(G[j]-G[i]+mod);
	for(RI i=1;i<=n;++i) if(G[i]) ++ans;
	printf("%d\n",ans);
	for(RI i=1;i<=n;++i) if(G[i]) printf("%d ",i);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值