LOJ6519 魔力环

题面:LOJ

解析

因为是等价环计数,考虑Burnside引理。

设f(i)表示将环分做\(n/i\)个循环的不动点个数。
发现对于\(f(i)\),其循环长度为\(i\),那么一定有\(i|m\)
即:\(i|gcd(n,m)\),否则没有贡献,所以:

\[Ans=\frac{\sum_{d|gcd(n,m)} f(d) \varphi(\frac{n}{d})}{n} \]

那么如何计算\(f(i)\)呢?此处为方便,特判\(n=m\)的情况,
然后问题就等价于\(i\)个小球成环,给其中\(\frac{mi}{n}\)个小球染色,连续不超过\(k\)个的方案数。

形式化地,令小球个数为\(x\),黑球个数为\(y\),答案为\(F(x,y)\)
注意到这里的环就没有等价问题了,所以考虑破环成链。

现在就是在\(x-y\)个白球里插入\(y\)个黑球,每个空隙不超过\(k\)个,
其中第一个白球前面与最后一个白球后面黑球之和亦不能超过\(k\)个。
现在枚举第一个白球与最后一个白球之间的黑球数量\(i\),有:

\[F(x,y)=\sum_{i=0}^{k} (i+1)T(y-i,x-y-1) \]

其中,因为环是有序号的,所以要枚举\(i\)个黑球的分配方式,有(i+1)种。

\(T(x,y)\)则表示将\(x\)个球,分配在\(y\)个盒子里(可以为空),每个盒子不超过\(k\)个的方案数。
现在考虑计算\(T(x,y)\),用容斥,每次强制\(i\)个盒子超出容量,那么有:

\[T(x,y)=\sum_{i=0}^{min(x/(k+1),y)} (-1)^i {y \choose i} H(x-i*(k+1),y) \]

其中,\(H(x,y)\)表示将\(x\)个相同的小球放入\(y\)个不同的盒子里(可以为空)的方案数,有:

\[H(x,y)={x+y-1 \choose y-1} \]

计算\(T(x,y)\)的复杂度是\(O(x/k)\),计算\(F(x,y)\)的复杂度是\(O(y)\),还要枚举约数,
所以计算\(Ans\)的复杂度就是\(O(n+\sigma(n))\),至此,问题圆满解决。

代码


#include<cstdio>
#define N 100005

using namespace std;

inline int In(){
	char c=getchar(); int x=0;
	for(;c<'0'||c>'9';c=getchar());
	for(;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
	return x;
}

const int P=998244353;

inline int min(int a,int b){
	return a<b?a:b;
}

inline int gcd(int a,int b){
	return (b==0)?a:gcd(b,a%b);
}

inline int power(int x,int k){
	if(!x) return 0;
	int s=1,t=x;
	for(;k;k>>=1,t=1ll*t*t%P)
	if(k&1) s=1ll*s*t%P;
	return s;
}

int n,m,K,Gcd,ans,phi[N],fac[N],inv[N],p[N],p_cnt=0; bool vis[N];

inline void Get_phi(){
	phi[1]=1;
    for(int i=2;i<=n;++i){
        if(!vis[i]) p[++p_cnt]=i,phi[i]=i-1;
        for(int j=1;j<=p_cnt;++j){
            if(i*p[j]>n) break; vis[i*p[j]]=1;
            if(i%p[j]==0){
                phi[i*p[j]]=phi[i]*p[j];
                break;
            }
            phi[i*p[j]]=phi[i]*(p[j]-1);
        }
    }
}

inline void Get_fac(){
	fac[0]=fac[1]=1;
	for(int i=2;i<=n;++i) fac[i]=1ll*fac[i-1]*i%P;
	inv[n]=power(fac[n],P-2);
	for(int i=n-1;~i;--i) inv[i]=1ll*inv[i+1]*(i+1)%P;
}

inline int C(int x,int y){ return 1ll*fac[x]*inv[y]%P*inv[x-y]%P; }
inline int H(int x,int y){ return C(x+y-1,y-1); }
inline int T(int x,int y){
	int res=0,lim=min(x/(K+1),y);
	for(int i=0,t=1;i<=lim;++i,t=P-t)
	res=(res+1ll*t*C(y,i)%P*H(x-i*(K+1),y)%P)%P;
	return res;
}
inline int F(int x,int y){
	if(x-y==1) return K>=(y-1)?x:0;
	int res=0,lim=min(y,K);
	for(int i=0;i<=lim;++i)
	res=(res+1ll*(i+1)*T(y-i,x-y-1)%P)%P;
	return res;
}

int main(){
	n=In(); m=In(); K=In(); ans=0;
	if(K==1&&m>n/2){ printf("%d\n",0); return 0;}
	if(n==m){ printf("%d\n",(K>=n)); return 0; } 
	Gcd=gcd(n,m); Get_phi(); Get_fac();
	for(int i=1;i<=Gcd;++i) if(Gcd%i==0)
	ans=(ans+1ll*F(n/i,m/i)*phi[i]%P)%P;
	printf("%lld\n",1ll*ans*power(n,P-2)%P);
	return 0;
}

posted @ 2019-05-27 20:50  pkh68  阅读(350)  评论(0编辑  收藏  举报