【Polya定理】SCU 4423

题目链接:http://cstest.scu.edu.cn/soj/problem.action?id=4423

题意:

环上N个点,2种颜色,只有旋转置换,有多少种染色方法


Polya定理


设\(G=\left \{ a_1,a_2,\cdots ,a_g \right \}\)是n 个对象的一个置换群,用k 种颜色去染这n 个对象,若一种染色方案在群G 的作
用下变为一种方案,则这两个方案当作是同一种方案,这样的不同染色方案数为:
$$L=\frac{1}{| G|}\sum_{i=1}^{|G|}k^{C(a_i)}$$
\(C(a_i)\)为置换的循环节,\(|G|\)表示群的置换数。

对于n 个位置的手镯,有n 种旋转置换和n 种翻转置换
对于旋转置换:
\(C(a_i)=\gcd (n,i)\),i 表示旋转i 颗宝石以后。\(i=0\) 时\(\gcd(n,0)=n\)
对于翻转置换:
如果\(n\)为偶数:则有\(\frac{n}{2}\)个置换\(C(a)=\frac {n}{2}\),有\(\frac{n}{2}\)个置换\(C(a)=\frac {n}{2}+1\)
如果\(n\)为奇数:则有\(n\)个置换\(C(a)=\frac {n}{2}+1\)



题目只有旋转置换,但要求\(1\leqslant n\leqslant 10^9\),直接算肯定会超时,所以可以枚举n的因子i,满足\(\\gcd(n,x)=i,1\leqslant x\leqslant n\)的x的数量为\(\phi(\frac{n}{i})\),\(\phi\)为欧拉函数
所以公式可化为
$$L=\frac{1}{n}\sum_{i|n}\phi (\frac{n}{i})\cdot k^i$$
其中幂的运算用快速幂,最后答案要求mod1000000007,所以还要求\(nx = 1( mod 1000000007)\) 的x值,就是逆元
对n的因子的枚举可以只枚举到\(\sqrt{n}\),因为\(i|n\),则\(\frac{n}{i}|n\)

最终复杂度为\(O(k^2)\),其中k为N的约数个数。


#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
#define MAX(a,b) ((a>b)?(a):(b))
#define MIN(a,b) ((a<b)?(a):(b))
#define LL long long
#define N 100005
#define INF 1<<30
#define MOD 1000000007



LL euler(LL n) {
    LL ans = n;
    for(int i = 2; i*i <= n; i++) {
        if(n % i == 0) {
            ans -= ans/i;
            while(n % i == 0)
                n /= i;
        }
    }
    if(n > 1)ans -= ans/n;
    return ans;
}

LL inv(LL a,LL m)//求ax = 1( mod m) 的x值,就是逆元(0<a<m)
{
    if(a == 1)return 1;
    return inv(m%a,m)*(m-m/a)%m;
}

LL pm(LL a,LL b) {
    LL ret=1;
    while(b) {
        if(b&1)ret=(ret*a)%MOD;
        a=(a*a)%MOD;
        b>>=1;
    }
    return ret;
}


int main() {
    //freopen("C:\\Users\\F\\Desktop\\in.txt", "r", stdin);
    //freopen("C:\\Users\\F\\Desktop\\out.txt", "w", stdout);
    int t;
    LL n;
    scanf("%d",&t);
    while(t--) {
        scanf("%lld",&n);
        LL ans=0;

        for(int i=1; i*i<=n; ++i) {
            if(n%i==0) {
                ans=(ans+pm(2,i)*euler(n/i))%MOD;
                if(i!=n/i){
                    ans=(ans+pm(2,n/i)*euler(i))%MOD;
                }
            }
        }
        printf("%lld\n",((ans)*inv(n,MOD))%MOD);

    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值