Polya定理
02.09 考试第一题是 Polya定理,洛谷上的模板 (P4980 【模板】Polya) 定理是求 n 点环染 n 种颜色的本质不同的方案数,而考试考的是 给定 n , m , 在 size 为 n 的环上染 m 种颜色的方案数
首先呢,这是到群论的题,直接上 Polya 定理的公式
\(\frac{1}{n} \sum_{i=1}^{n} { m^{xi} }\)
其中 n 为置换数, m 为染色的种类数, xi 为每个置换的循环数
这是公式
关于这道题, 根据找规律每个置换的循环数是 __gcd(i , n) , 所以暴力是 O(n t) 的 (t 为多组数据)
所以我们要优化,改为枚举 Gcd
所以公式改为
\(\frac{1}{n} \sum_{d|n} \phi(n/d) m^d\)
看起来就是狄利克雷卷积,其实暴力就可以过了
再就是根号n的求 phi 函数
公式为
\(\phi(n) = n \prod_{i = 1}^{k}(1-\frac{pi-1}{pi})\)
我们根号n 的枚举 i , 每次都先得到的就是质因子 , 再把 n 的质因子从n中消去,剩下的就是更大的质因子了,类似于筛质数的操作
代码:
#include <bits/stdc++.h>
#define ll long long
#define int ll
#define E 100007
#define M 1000000007
using namespace std ;
ll read() {
ll s = 0 , f = 1 ; char ch ;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -1) ;
for(s = ch - '0' ; isdigit(ch = getchar()) ; s = s * 10 + ch - '0') ;
return s * f ;
}
void putit(ll x) {
if(x < 0) putchar('-') , x = -x ;
if(x > 9) putit(x / 10) ;
putchar(x % 10 + '0') ;
}
ll ksm(ll d , ll z) {
ll res = 1 ;
while(z) {
if(z & 1) res = res * d % M ;
d = d * d % M ;
z >>= 1 ;
}
return res ;
}
ll n , m ;
ll P(ll xx) { return ksm(m , xx) ; }
ll phi(ll x) {
ll sqr = sqrt(x) , rt = x ;
for(int i = 2 ; i <= sqr ; i ++) {
if(x % i == 0) {
rt = rt / i * (i-1) ;
while(x % i == 0) x /= i ;
}
}
if(x != 1) rt = rt / x * (x-1) ;
return rt ;
}
signed main() {
freopen("ak.in" , "r" , stdin) ;
freopen("ak.out", "w" ,stdout) ;
ll T = read() ;
while(T --) {
n = read() , m = read() ;
ll rt = 0 , sqr = sqrt(n) ;
for(int i = 1 ; i <= sqr ; i ++) {
if(n % i != 0) continue ;
ll d = i ;
(rt += phi(n/d) * P(d) % M) %= M ;
if(d * d == n) continue ;
d = n / i ;
(rt += phi(n/d) * P(d) % M) %= M ;
}
(rt *= ksm(n , M-2)) %= M ;
putit(rt) , puts("") ;
}
fclose(stdin) , fclose(stdout) ;
return 0 ;
}
/*
5
5 3
6 2
8 4
20 9
30 11
*/
/*
51
14
8230
872010021
822671761
*/