【洛谷P4709】信息传递(置换)(组合数学)(多项式Exp)

传送门


题解:

很早之前看到过这道题,当时连置换是什么都不知道。。。

首先考虑置换 g g g中的某一个长为 S S S的循环环,学过群论的应该知道,在置换自乘 n n n次之后,这个循环会变为 g c d ( n , S ) gcd(n,S) gcd(n,S)个循环。

考虑将 t t t个长为 S S S的循环拼接成一个,使得若干次自乘之后这个循环断裂为 t t t个长为 S S S的循环。翻过任何一本群论教材就知道,最后在同一循环中的元素是那些只与初始下标 % S \%S %S的值有关,所以这一部分的方案数为 S t − 1 ( t − 1 ) ! S^{t-1}(t-1)! St1(t1)!,其实就是考虑一个圆排列。

所有长度为 S S S的循环显然可以放在一起处理。

考虑 e x p exp exp的组合意义,显然我们对于每个长度求出拿 t t t个该长度的串来拼接成一个的方案数的 E G F EGF EGF,设为 A ( x ) A(x) A(x),则拿出 c n t [ S ] cnt[S] cnt[S]个循环,拼接成若干个的方案数的生成函数为 exp ⁡ ( A ( x ) ) \exp(A(x)) exp(A(x)),复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn),而且不好卡满。

然而这个玩意可以直接 D P DP DP,复杂度 O ( n 4 3 ) O(n^{\frac{4}{3}}) O(n34),同样,几乎卡不满


代码(DP):

#include
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;
		while(!isdigit(c=gc()));T num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int getint(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;

cs int mod=998244353;
inline int add(int a,int b){return (a+=b)>=mod?a-mod:a;}
inline int dec(int a,int b){return (a-=b)<0?a+mod:a;}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b,int res=1){
	for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));
	return res;
}
inline void Inc(int &a,int b){(a+=b)>=mod&&(a-=mod);}
inline void Dec(int &a,int b){(a-=b)<0&&(a+=mod);}

inline int gcd(int a,int b){
#define ctz __builtin_ctz
	int shift=ctz(a|b);
	for(b>>=ctz(b);a;a-=b)if((a>>=ctz(a))<b)std::swap(a,b);
	return b<<=shift;
}

cs int N=1e5+5;

int n;

int fac[N],ifac[N];
inline void init(){
	fac[0]=fac[1]=ifac[0]=1;
	for(int re i=2;i<=n;++i)fac[i]=mul(fac[i-1],i);
	ifac[n]=power(fac[n],mod-2);
	for(int re i=n-1;i;--i)ifac[i]=mul(ifac[i+1],i+1);
}

inline int C(int n,int m){return (n>=0&&m>=0&&n>=m)?mul(fac[n],mul(ifac[m],ifac[n-m])):0;}

std::vector<int> factor;

int to[N],cnt[N];
bool vis[N];

int dp[N],coef[N];

signed main(){
#ifdef zxyoi
	freopen("message.in","r",stdin);
#endif
	n=getint();init();
	for(int re i=1;i<=n;++i)to[i]=getint();
	for(int re i=1;i<=n;++i)if(!vis[i]){
		int len=0,x=i;
		while(!vis[x]){
			vis[x]=true;
			++len;x=to[x];
		}
		++cnt[len];
	}
	for(int re i=1;i*i<=n;++i)
	if(n%i==0){
		factor.push_back(i);
		if(i*i!=n)factor.push_back(n/i);
	}
	std::sort(factor.begin(),factor.end());
	int ans=1;
	for(int re i=1;i<=n&&ans;++i)if(cnt[i]){
		coef[1]=1;
		for(int re j=2;j<=cnt[i];++j)coef[j]=mul(coef[j-1],mul(i,j-1));
		memset(dp+1,0,sizeof(int)*cnt[i]);
		dp[0]=1;
		for(int re j=1;j<=cnt[i];++j){
			for(int re t:factor){
				if(t>j)break;
				if(gcd(n,i*t)!=t)continue;
				Inc(dp[j],mul(dp[j-t],mul(C(j-1,t-1),coef[t])));
			}
		}
		ans=mul(ans,dp[cnt[i]]);
	}
	cout<<ans<<"\n";
	return 0;
}

代码exp:

#include
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;
		while(!isdigit(c=gc()));T num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int getint(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;

inline int gcd(int a,int b){
#define ctz __builtin_ctz
	int shift=ctz(a|b);
	for(b>>=ctz(b);a;a-=b)if((a>>=ctz(a))<b)std::swap(a,b);
	return b<<=shift;
}

cs int mod=998244353;
inline int add(int a,int b){return (a+=b)>=mod?a-mod:a;}
inline int dec(int a,int b){return (a-=b)<0?a+mod:a;}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b,int res=1){
	for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));
	return res;
}
inline void Inc(int &a,int b){(a+=b)>=mod&&(a-=mod);}
inline void Dec(int &a,int b){(a-=b)<0&&(a+=mod);}

cs int SIZE=1<<19|1,bit=19;

typedef std::vector<int> Poly;

int r[SIZE],*w[bit+1];
inline void init_NTT(){
	for(int re i=1;i<=bit;++i)w[i]=new int[1<<i-1];
	int wn=power(3,(mod-1)>>bit);
	w[bit][0]=1;
	for(int re i=1;i<(1<<bit-1);++i)w[bit][i]=mul(w[bit][i-1],wn);
	for(int re i=bit-1;i;--i)
	for(int re j=0;j<(1<<i-1);++j)w[i][j]=w[i+1][j<<1];
}
inline void NTT(Poly &A,int len,int typ){
	for(int re i=0;i<len;++i)if(i<r[i])std::swap(A[i],A[r[i]]);
	for(int re i=1,t=1;i<len;i<<=1,++t)
	for(int re j=0;j<len;j+=i<<1)
	for(int re k=0;k<i;++k){
		int x=A[j+k],y=mul(A[j+k+i],w[t][k]);
		A[j+k]=add(x,y),A[j+k+i]=dec(x,y);
	}
	if(typ==-1){
		std::reverse(A.begin()+1,A.begin()+len);
		for(int re i=0,inv=power(len,mod-2);i<len;++i)A[i]=mul(A[i],inv);
	}
}
inline void init_rev(int len){
	for(int re i=0;i<len;++i)r[i]=r[i>>1]>>1|((i&1)?len>>1:0);
}

int fac[SIZE],ifac[SIZE],inv[SIZE];
inline void init_inv(){
	fac[0]=fac[1]=ifac[0]=ifac[1]=inv[0]=inv[1]=1;
	for(int re i=2;i<SIZE;++i){
		fac[i]=mul(fac[i-1],i);
		inv[i]=mul(inv[mod%i],mod-mod/i);
		ifac[i]=mul(ifac[i-1],inv[i]);
	}
}

inline int C(int n,int m){return (n>=0&&m>=0&&n>=m)?mul(fac[n],mul(ifac[m],ifac[n-m])):0;}

inline Poly operator*(Poly a,Poly b){
	int deg=a.size()+b.size()-1,l=1;
	while(l<deg)l<<=1;init_rev(l);
	a.resize(l);NTT(a,l,1);
	b.resize(l);NTT(b,l,1);
	for(int re i=0;i<l;++i)a[i]=mul(a[i],b[i]);
	NTT(a,l,-1);a.resize(deg);
	return a;
}

inline Poly Deriv(Poly a){
	for(int re i=0;i+1<a.size();++i)a[i]=mul(a[i+1],i+1);
	a.pop_back();return a;
}

inline Poly Integ(Poly a){
	a.push_back(0);
	for(int re i=a.size()-1;i;--i)a[i]=mul(a[i-1],inv[i]);
	a[0]=0;return a;
}

inline Poly Inv(cs Poly &a,int lim){
	Poly c,b(1,power(a[0],mod-2));
	for(int re l=4;(l>>2)<lim;l<<=1){
		init_rev(l);
		c=a,c.resize(l>>1);
		c.resize(l),NTT(c,l,1);
		b.resize(l),NTT(b,l,1);
		for(int re i=0;i<l;++i)b[i]=mul(b[i],dec(2,mul(b[i],c[i])));
		NTT(b,l,-1);b.resize(l>>1);
	}b.resize(lim);
	return b;
}

inline Poly Ln(Poly a,int lim){
	a=Integ(Deriv(a)*Inv(a,lim));
	a.resize(lim);
	return a;
}

inline Poly Exp(cs Poly &a){
	Poly c,b(1,1);int n=a.size();
	for(int re i=2;(i>>1)<n;i<<=1){
		c=Ln(b,i);
		for(int re j=0;j<i;++j)c[j]=dec(j<n?a[j]:0,c[j]);
		c[0]=add(c[0],1);
		b=b*c;b.resize(i);
	}b.resize(n);
	return b;
}

cs int N=1e5+5;

int n;

std::vector<int> factor;

int to[N],cnt[N];
bool vis[N];

Poly f,g;

signed main(){
	init_NTT();
	init_inv();
#ifdef zxyoi
	freopen("message.in","r",stdin);
#endif
	n=getint();
	for(int re i=1;i<=n;++i)to[i]=getint();
	for(int re i=1;i<=n;++i)if(!vis[i]){
		int len=0,x=i;
		while(!vis[x]){
			vis[x]=true;
			++len;x=to[x];
		}
		++cnt[len];
	}
	for(int re i=1;i*i<=n;++i)
	if(n%i==0){
		factor.push_back(i);
		if(i*i!=n)factor.push_back(n/i);
	}
	std::sort(factor.begin(),factor.end());
	int ans=1;
	for(int re i=1;i<=n&&ans;++i)if(cnt[i]){
		f=Poly(cnt[i]+1,0);
		for(int re j:factor){
			if(j>cnt[i])break;
			if(gcd(j*i,n)!=j)continue;
			f[j]=power(i,j-1,inv[j]);
		}
		g=Exp(f);
		ans=mul(ans,mul(g[cnt[i]],fac[cnt[i]]));
	}
	cout<<ans<<"\n";
	return 0;
}

你可能感兴趣的:(生成函数,多项式,置换群)