zoukankan      html  css  js  c++  java
  • 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
    ",0); return 0;}
    	if(n==m){ printf("%d
    ",(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
    ",1ll*ans*power(n,P-2)%P);
    	return 0;
    }
    
    
  • 相关阅读:
    高精度计算
    高精度除以低精度
    P1258 小车问题
    POJ 2352 stars (树状数组入门经典!!!)
    HDU 3635 Dragon Balls(超级经典的带权并查集!!!新手入门)
    HDU 3938 Portal (离线并查集,此题思路很强!!!,得到所谓的距离很巧妙)
    POJ 1703 Find them, Catch them(确定元素归属集合的并查集)
    HDU Virtual Friends(超级经典的带权并查集)
    HDU 3047 Zjnu Stadium(带权并查集,难想到)
    HDU 3038 How Many Answers Are Wrong(带权并查集,真的很难想到是个并查集!!!)
  • 原文地址:https://www.cnblogs.com/pkh68/p/10933118.html
Copyright © 2011-2022 走看看