【洛谷 P4980】 【模板】Pólya 定理

简单的写一下吧,Polya定理就是用来求等价类的数目的。
什么是等价类?经过某些置换操作能相等的两个的状态属于同一个等价类。
然后是一些基本的定理,
等价类的数目(C)为置换群的不动点的平均值,
(C=frac{1}{|G|}sum _{fin G}k^{m(f)})
其中,(G)为置换群,(f)为其中的一种置换,(m(f))表示把置换(f)看成(m(f))(f)的乘积(可以理解为循环节),(k)表示颜色的种类数。
显然本题的置换群就是({旋转0个,旋转1个,...,旋转n个})
考虑旋转(k)个的循环节(p),显然(p|k),同时(p|n),所以旋转(k)个的贡献就是(n^{gcd(k,n)})
那么最终答案就是(frac{1}{n}sum_{i=0}^{n-1}n^{gcd(i,n)})
直接求显然是(O(nlogn))
(n)的约数为(m)个,考虑到不同(gcd)的数量只有(m)个,于是枚举约数(d)
(k=e*d),显然满足(gcd(k,n)=d)(e)要与(frac{n}{d})互质,那么这样的(k)(varphi{frac{n}{d}})
于是最终答案就是(frac{1}{n}sum_{d|n}n^dvarphi{frac{n}{d}})

#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
int T, n;
int gcd(int a, int b){
	return !b ? a : gcd(b, a % b);
}
long long ans;
long long fast_pow(long long a, int k){
	long long ans = 1;
	while(k){
		if(k & 1) ans = ans * a % MOD;
		a = a * a % MOD; k >>= 1;
	}
	return ans;
}
int phi(int x){
	if(x == 1) return 1;
	int y = x;
	int p = sqrt(x);
	for(int i = 2; i <= p && x != 1; ++i)
		if(x % i == 0){
			y = y / i * (i - 1);
			while(x % i == 0) x /= i;
		}
	if(x != 1) y = y / x * (x - 1);
	return y;
}
int main(){
	scanf("%d", &T);
	while(T--){
		scanf("%d", &n); ans = 0;
		int p = sqrt(n);
		for(int i = 1; i <= p; ++i)
			if(n % i == 0){
				ans = (ans + fast_pow(n, i) * phi(n / i)) % MOD;
				if(i * i != n) ans = (ans + fast_pow(n, n / i) * phi(i)) % MOD;
			}
		printf("%lld
", ans * fast_pow(n, MOD - 2) % MOD);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/Qihoo360/p/15286908.html