• P4980-[模板]Pólya定理


    正题

    题目链接:https://www.luogu.com.cn/problem/P4980


    题目大意

    (n)个物品图上(m)种颜色,求在可以旋转的情况下本质不同的涂色方案。


    解题思路

    既然是群论基本题就顺便写一下刚刚了解到的相关知识把(顺便消磨一下时间

    一个群((G, imes ))定义为一个在运算( imes)下满足以下条件的集合

    1. 封闭性:若存在(a,bin G)那么有(a imes bin G)
    2. 交换律:若有(a,b,cin G)那么有((a imes b) imes c=a imes (b imes c))
    3. 单位元:群中(exists ein G)满足(forall xin G)都有(x imes e=x)
    4. 逆元:对于(forall xin G)都有一个唯一元素(yin G)(x imes y=e)

    然后中间一些东西很多很杂这里不多说了,直接到置换部分。

    一般来说规定置换第一行为((1,2,3...)),那么定义一个置换(sigma=(g_1,g_2,g_3,...))。如果一个置换作用与一个排列(a),一般写为(sigma(a)=b)的话,就有(b_i=a_{g_i})。需要注意的是对于一个置换两次后相当与使用了另一个置换。(也就是置换只能生效一次

    然后就是( ext{Burnside})引理了,对于一个置换群(G),若(G)作用与一个集合(X)时,集合(X)中本质不同的元素个数为

    [frac{1}{|G|}sum_{fin G}C(f) ]

    其中(C(f))表示(X)的所有元素中对于置换(f)的不动点数量。

    ( ext{Polya})定理就是建立在( ext{Burnside})引理上的,对于一个置换(f),定义它的循环节数量为(T(f)),用(m)种颜色染色时方本质不同的染色方案数就是

    [frac{1}{|G|}sum_{fin G}m^{T(f)} ]

    也就是(m^{T(f)}=C(f)),这个很显然,因为每个循环节涂成一种颜色就是一个不动点。

    回到这题的旋转来,我们可以将其视为(n)个不同的置换构成的一个置换群。对于旋转(i)步,它的循环节数量就是(gcd(n,i)),也就是我们要求

    [frac{1}{n}sum_{i=0}^{n-1}m^{gcd(n,i)} ]

    枚举一下(gcd(n,i))

    [frac{1}{n}sum_{d|n}m^dsum_{i=1}^{frac{n}{d}}[gcd(frac{n}{d},i)==1] ]

    哦对啊好像有(m=n)

    [frac{1}{n}sum_{d|n}n^dvarphi(frac{n}{d})=sum_{d|n}n^{d-1}varphi(frac{n}{d}) ]

    这个时间复杂度大概是(O(Tn^{frac{3}{4}}))的,但是因为约数个数远到不了(sqrt n)所以你可以把它视为常数比较大的(O(Tsqrt n))


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const ll P=1e9+7;
    ll T,n,ans;
    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 phi(ll x){
        ll ans=x;
        for(ll i=2;i*i<=x;i++){
            if(x%i)continue;
            while(x%i==0)x/=i;
            ans=ans/i*(i-1);
        }
        if(x>1)ans=ans/x*(x-1);
        return ans;
    }
    ll calc(ll x)
    {return phi(x)*power(n,n/x-1)%P;}
    signed main()
    {
        scanf("%lld",&T);
        while(T--){
            scanf("%lld",&n);ans=0;
            for(ll i=1;i*i<=n;i++){
                if(n%i)continue;
                ans=(ans+calc(i))%P;
                if(i*i!=n)ans=(ans+calc(n/i))%P;
            }
            printf("%lld
    ",ans);
        }
        return 0;   
    }
    
  • 相关阅读:
    熟悉常用的HDFS操作
    爬虫大作业-爬取B站弹幕
    数据结构化与保存
    使用正则表达式,取得点击次数,函数抽离
    爬取校园新闻首页的新闻
    网络爬虫基础练习
    综合练习:词频统计
    理解MapReduce
    熟悉常用的HBase操作
    熟悉常用的HDFS操作
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14269213.html
Copyright © 2020-2023  润新知