【算法讲27:Polya定理】BurnSide 引理 Polya定理 | 洛谷 P4980

  • 注:下文证明都略,只记录重要的性质结论

前置

  • 群,置换
    下文的记号
    g g g 置换
    G G G 置换集
    ∣ G ∣ |G| G 置换集大小
    ( a , b , c , ⋯   ) (a,b,c,\cdots) (a,b,c,) 一个置换

引入

  • 洛谷 P4980
    一个串,上面有 N N N 个珠子,用 N N N 种颜色给它染色
    求有多少个本质不同的染色方案
    本质相同:可以通过旋转,让两串珠子相同
  • 样例数 1 ≤ T ≤ 1000 1\le T\le 1000 1T1000
    1 ≤ N ≤ 1 0 9 1\le N\le 10^9 1N109

前置: B u r n S i d e BurnSide BurnSide 引理

  • 不过貌似讲讲都挺麻烦的…
    把祖传图给过来
    请添加图片描述
  • 问这样的 2 × 2 2\times2 2×2 的矩形,每个块染色 0 / 1 0/1 0/1,有多少种本质不同的染色
    本质相同的染色:通过旋转 得到相同的块算本质相同
  • 虽然貌似有 16 16 16染色方案,但是我们通过如下的操作,某种方案会转换成某种方案:
    顺 时 针 旋 转 0 ° : ( 1 ) ( 2 ) ⋯ ( 16 ) 逆 时 针 旋 转 90 ° : ( 1 ) ( 2 ) ( 3 , 4 , 5 , 6 ) ( 7 , 8 , 9 , 10 ) ( 11 , 12 ) ( 13 , 14 , 15 , 16 ) 顺 时 针 旋 转 90 ° : ( 1 ) ( 2 ) ( 6 , 5 , 4 , 3 ) ( 10 , 9 , 8 , 7 ) ( 12 , 11 ) ( 16 , 15 , 14 , 13 ) 旋 转 180 ° : ( 1 ) ( 2 ) ( 3 , 5 ) ( 4 , 6 ) ( 7 , 9 ) ( 8 , 10 ) ( 11 ) ( 12 ) ( 13 , 15 ) ( 14 , 16 ) \begin{aligned} &顺时针旋转0\degree:&(1)(2)\cdots(16)\\ &逆时针旋转90\degree:&(1)(2)(3,4,5,6)(7,8,9,10)(11,12)(13,14,15,16)\\ &顺时针旋转90\degree:&(1)(2)(6,5,4,3)(10,9,8,7)(12,11)(16,15,14,13)\\ &旋转180\degree:&(1)(2)(3,5)(4,6)(7,9)(8,10)(11)(12)(13,15)(14,16)\\ \end{aligned} 0°90°90°180°(1)(2)(16)(1)(2)(3,4,5,6)(7,8,9,10)(11,12)(13,14,15,16)(1)(2)(6,5,4,3)(10,9,8,7)(12,11)(16,15,14,13)(1)(2)(3,5)(4,6)(7,9)(8,10)(11)(12)(13,15)(14,16)
    其中 ( a 1 , a 2 , ⋯   , a k ) (a_1,a_2,\cdots,a_k) (a1,a2,,ak) 表示一个置换群
  • B u r n S i d e BurnSide BurnSide 引理:
    ∣ X / G ∣ = 1 ∣ G ∣ ∑ g ∈ G ∣ X g ∣ |X/G|=\frac{1}{|G|}\sum_{g\in G}|X^g| X/G=G1gGXg
    正规表述:轨道数 = = = G G G一个元素保持不动的点的个数的平均值
    见:百度百科
  • 在这里 2 × 2 2\times2 2×2 矩阵染色问题中, ∣ X / G ∣ = 16 + 2 + 2 + 4 4 = 6 |X/G|=\frac{16+2+2+4}{4}=6 X/G=416+2+2+4=6,故只有 6 6 6 种合法染色方案

Polya 定理

  • 由于我们太难把所有的置换等价操作给求出来(太多了…) 故有更优化的 P o l y a Polya Polya 定理
    它的关键就是给出计算不动点的个数的方法
  • P o l y a Polya Polya 定理:
    A n s = 1 ∣ G ∣ ∑ g ∈ G m c ( g ) Ans=\frac{1}{|G|}\sum_{g\in G}m^{c(g)} Ans=G1gGmc(g)
    表述:对于有 m m m 种颜色染色的问题, ∣ X g ∣ = m c ( g ) |X^g|=m^{c(g)} Xg=mc(g)
    其中 c ( g ) c(g) c(g) 表示在置换 g g g 的情况下,不动点的个数
  • 在这里 2 × 2 2\times2 2×2 矩阵染色问题中,查看四个格子的置换关系
    顺 时 针 旋 转 0 ° : ( 1 ) ( 2 ) ( 3 ) ( 4 ) 顺 时 针 旋 转 90 ° : ( 4 , 3 , 2 , 1 ) 顺 时 针 旋 转 180 ° : ( 1 , 3 ) ( 2 , 4 ) 顺 时 针 旋 转 270 ° : ( 1 , 2 , 3 , 4 ) \begin{aligned} &顺时针旋转0\degree:&(1)(2)(3)(4)\\ &顺时针旋转90\degree:&(4,3,2,1)\\ &顺时针旋转180\degree:&(1,3)(2,4)\\ &顺时针旋转270\degree:&(1,2,3,4)\\ \end{aligned} 0°90°180°270°(1)(2)(3)(4)(4,3,2,1)(1,3)(2,4)(1,2,3,4)
    于是 A n s = 1 4 × ( 2 4 + 2 1 + 2 1 + 2 2 ) = 6 Ans=\frac{1}{4}\times(2^4+2^1+2^1+2^2)=6 Ans=41×(24+21+21+22)=6 ,答案也是一样的

回到题目

  • 我们直接使用 P o l y a Polya Polya 定理,对于本质相同的操作,只有旋转操作
    旋转操作有 n n n 种,就是顺时针旋转 ( 0 ∼ n − 1 ) (0\sim n-1) (0n1) 个珠子 ,当然也可以写成旋转 ( 1 ∼ n ) (1\sim n) (1n) 个珠子
  • 考虑,如果我们顺时针旋转 k k k 个珠子,会获得多少个不动点
    根据我们的数学知识,答案为 gcd ⁡ ( k , n ) \gcd(k,n) gcd(k,n)
    那么答案就是
    A n s = 1 ∣ n ∣ ∑ k = 1 n n gcd ⁡ ( k , n ) Ans=\frac{1}{|n|}\sum_{k=1}^n n^{\gcd(k,n)} Ans=n1k=1nngcd(k,n)
  • 考虑到 n n n 非常大,我们不能直接枚举,我们像做莫比乌斯反演一样,套路化做:
    A n s = 1 n ∑ d ∣ n n d ∑ i = 1 n [ gcd ⁡ ( k , n ) = d ] = 1 n ∑ d ∣ n n d ∑ i = 1 ⌊ n d ⌋ [ gcd ⁡ ( k , n d ) = 1 ] = 1 n ∑ d ∣ n n d × φ ( n d ) \begin{aligned} Ans&=\frac{1}{n}\sum_{d|n}n^d\sum_{i=1}^n\Big[ \gcd(k,n)=d \Big]\\ &=\frac{1}{n}\sum_{d|n}n^d\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\Big[ \gcd(k,\frac{n}{d})=1 \Big]\\ &=\frac{1}{n}\sum_{d|n}n^d\times\varphi(\frac{n}{d}) \end{aligned} Ans=n1dnndi=1n[gcd(k,n)=d]=n1dnndi=1dn[gcd(k,dn)=1]=n1dnnd×φ(dn)
  • 暴力枚举,时间复杂度为 O ( T × n 3 / 4 ) O(T\times n^{3/4}) O(T×n3/4),差不多也能过

代码

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
typedef long long ll;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}

const int MAX = 2e5+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;

ll qpow(ll a,ll n){/* */ll res = 1LL;while(n){if(n&1)res=res*a%MOD;a=a*a%MOD;n>>=1;}return res;}
ll qpow(ll a,ll n,ll p){a%=p;ll res = 1LL;while(n){if(n&1)res=res*a%p;a=a*a%p;n>>=1;}return res;}
ll npow(ll a,ll n){/* */ll res = 1LL;while(n){if(n&1)res=res*a;a=a*a;n>>=1;if(res<0||a<0)return 0;}return res;}
ll inv(ll a){/* */return qpow(a,MOD-2);}
ll inv(ll a,ll p){return qpow(a,p-2,p);}


ll phi(ll n){
    ll res = n;
    for(int i = 2;i * i <= n;++i){
        if(n % i == 0){
            res = res * (1 - inv(i) + MOD) % MOD;
            while(n % i == 0)n /= i;
        }
    }
    if(n > 1)res = res * (1 - inv(n) + MOD) % MOD;
    return (res + MOD) % MOD;
}


int main()
{
    IOS;
    int T;cin >> T;
    while(T--){
        int n;cin >> n;
        ll res = 0;
        for(int i = 1;i * i <= n;++i){
            if(n % i == 0){
                res = (res + qpow(n,i) * phi(n / i) % MOD) % MOD;
                if(i * i != n)res = (res + qpow(n,n/i) * phi(i) % MOD) % MOD;
            }
        }
        res = res * inv(n) % MOD;
        cout << res << endl;
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值