• hdu7207-Find different【burnside引理】


    正题

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=7207


    题目大意

    一个序列 a a a,和它相同的序列当且仅当能通过以下操作实现相同:

    • a 1 a_1 a1丢到 a n a_n an,其余的向前移动一位。
    • 令所有 a i = ( a i + 1 ) % m a_i=(a_i+1)\%m ai=(ai+1)%m

    对于 n ∈ [ 1 , N ] n\in [1,N] n[1,N],求有多少个不同的序列。

    1 ≤ T ≤ 100 , 1 ≤ N , m ≤ 1 0 5 , ∑ N ≤ 1 0 6 1\leq T\leq 100,1\leq N,m\leq 10^5,\sum N\leq 10^6 1T100,1N,m105,N106


    解题思路

    根据 burnside \text{burnside} burnside引理,我们要找所有置换的不动点数量和。

    置换总共有 n × m n\times m n×m种,假设一种为循环位移了 x x x步,且所有数字加上了 y y y

    那么我们有 a i ≡ a ( i + x ) % n + y ( m o d m ) a_i\equiv a_{(i+x)\%n}+y\pmod m aia(i+x)%n+y(modm),从一个数开始一直加 x x x n n n,我们知道会产生 gcd ⁡ ( n , x ) \gcd(n,x) gcd(n,x)个环,对于每个环来说总共加了 n gcd ⁡ ( n , x ) \frac{n}{\gcd(n,x)} gcd(n,x)n y y y,最终又走回了起点。

    也就是对于这个 y y y来说合法的条件当且仅当 y × n gcd ⁡ ( n , x ) ≡ 1 ( m o d m ) y\times \frac{n}{\gcd(n,x)}\equiv 1\pmod m y×gcd(n,x)n1(modm),不难得到合法的 y y y的数量就是 gcd ⁡ ( m , n gcd ⁡ ( n , x ) ) \gcd(m,\frac{n}{\gcd(n,x)}) gcd(m,gcd(n,x)n)

    所以答案就是
    1 n m ∑ i = 0 n − 1 gcd ⁡ ( m , n gcd ⁡ ( n , x ) ) gcd ⁡ ( n , i ) \frac{1}{nm}\sum_{i=0}^{n-1}\gcd(m,\frac{n}{\gcd(n,x)})^{\gcd(n,i)} nm1i=0n1gcd(m,gcd(n,x)n)gcd(n,i)
    1 n m ∑ d ∣ n n φ ( n d ) gcd ⁡ ( m , n d ) d \frac{1}{nm}\sum_{d|n}^n\varphi(\frac{n}{d})\gcd(m,\frac{n}{d})^{d} nm1dnnφ(dn)gcd(m,dn)d

    时间复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn)


    code

    #pragma GCC optimize(2)
    %:pragma GCC optimize(3)
    %:pragma GCC optimize("Ofast")
    %:pragma GCC optimize("inline")
    #include
    #include
    #include
    #define ll long long
    using namespace std;
    const ll N=1e5+10,P=998244353;
    ll T,n,m,cnt,pri[N],phi[N],ans[N];
    bool v[N];
    ll power(ll x,ll b){
        ll ans=1;
        while(b){
            if(b&1)ans=ans*x%P;
            x=x*x%P;b>>=1;
        }
        return ans;
    }
    ll gcd(ll x,ll y)
    {return y?gcd(y,x%y):x;}
    void Prime(){
    	phi[1]=1;
    	for(ll i=2;i<N;i++){
    		if(!v[i])phi[i]=i-1,pri[++cnt]=i;
    		for(ll j=1;j<=cnt&&i*pri[j]<N;j++){
    			v[i*pri[j]]=1;
    			if(i%pri[j]==0){
    				phi[i*pri[j]]=phi[i]*pri[j];
    				break;
    			}
    			phi[i*pri[j]]=phi[i]*phi[pri[j]];
    		}
    	}
    	return;
    }
    signed main()
    {
    	Prime();
        scanf("%lld",&T);
        while(T--){
            scanf("%lld%lld",&n,&m);
            ll now=1,z=1;
            for(ll i=1;i<=n;i++){
            	z=z*m%P;
            	for(ll j=i;j<=n;j+=i)
            		(ans[j]+=z*phi[j/i]%P*gcd(m,j/i)%P)%=P; 
    		}
    		ll inv=power(m,P-2)%P;
    		for(ll i=1;i<=n;i++){
    			ans[i]=ans[i]*power(i,P-2)%P*inv%P;
    			printf("%lld%c",ans[i],(i==n)?'\n':' ');
    			ans[i]=0;
    		}
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
  • 相关阅读:
    启动bert-server报错TypeError: cannot unpack non-iterable NoneType object
    配application.xml属性
    java中的复杂查询sql语句怎么写?
    基因组大小查询(二)|基因组组装结果查询
    两天两夜,1M图片优化到100kb!
    基于SpringBoot+vue的文件管理系统
    c++运算符重载实现
    缓存知识总结
    工具 - markdown编辑器常用方法
    基于拦截器的后端资源权限实现
  • 原文地址:https://blog.csdn.net/Mr_wuyongcong/article/details/126329092