P4980 模板Polya定理
Posted olinr
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4980 模板Polya定理相关的知识,希望对你有一定的参考价值。
(color{#0066ff}{ 题目描述 })
给定一个(n)个点,(n)条边的环,有(n)种颜色,给每个顶点染色,问有多少种本质不同的染色方案,答案对(10^9+7)取模
注意本题的本质不同,定义为:只需要不能通过旋转与别的染色方案相同。
(color{#0066ff}{输入格式})
第一行输入一个(t),表示有(t)组数据
第二行开始,一共(t)行,每行一个整数(n),意思如题所示。
(color{#0066ff}{输出格式})
共(t)行,每行一个数字,表示染色方案数对(10^9+7)取模后的结果
(color{#0066ff}{输入样例})
5
1
2
3
4
5
(color{#0066ff}{输出样例})
1
3
11
70
629
(color{#0066ff}{数据范围与提示})
(n leq 10^9)(,t leq 10^3)
(color{#0066ff}{ 题解 })
前置知识
1、置换
就是将元素的对应以表格形式来表示
标准形式为
(left(egin{aligned}1 && 2 && 3 && 4 && dots && n \ a_1 && a_2 && a_3 && a_4 && dots && a_nend{aligned} ight))
其中(a_1dots a_n)为(1-n)的一个排列
相当于经过变换,原来1的位置到了(a_1),2的位置到了(a_2)。。。
根据题意,本质不同即为旋转
于是单次置换为
(left(egin{aligned}1 && 2 && 3 && 4 && dots && n \ 2 && 3 && 4 && 5 && dots && 1end{aligned} ight))
二次置换为
(left(egin{aligned}1 && 2 && 3 && 4 && dots && n \ 3 && 4 && 5 && 6 && dots && 2end{aligned} ight))
2、Burnside引理
对于每个置换f,我们定义C(f)为在置换f下保持不变的方案数。
则有: 本质不同的方案数为C(f)的平均数。
(ans=frac{1}{left | G ight |} sum _{f in G})
3、Polya定理
(egin{aligned}ans=frac 1 n sum_{i=1}^n m^{x_i}end{aligned})
其中,n为置换长度,m是染色种类,(x_i)是i次置换的循环个数
循环个数是啥呢
比如上面的一次置换,个数为1,(1 o2 o3 o4 o5 o1)
比如长度为6的三次置换,个数为3
(left(egin{aligned}1 && 2 && 3 && 4 && 5 && 6\ 4 && 5 && 6 && 1 && 2 && 3end{aligned} ight))
第一个循环(1 o4 o1)
第二个循环(2 o5 o2)
第三个循环(3 o6 o3)
不难发现,(x_i=gcd(i,n))
于是改为枚举n的因子
(egin{aligned}ans=frac 1 n sum_{i=1}^n m^{gcd(i,n)}end{aligned})
(egin{aligned}ans=frac 1 n sum_{d|n}^n varphi(frac n d)* m^{d}end{aligned})
表面上这东西的复杂度是(O(T*(sqrt n)^2)=O(Tn))的
但实际上,求(varphi)的根号只会被调用log次
#include<bits/stdc++.h>
#define LL long long
LL in() {
char ch; LL x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
const int mod = 1e9 + 7;
LL ksm(LL x, LL y) {
LL re = 1LL;
while(y) {
if(y & 1) re = re * x % mod;
x = x * x % mod;
y >>= 1;
}
return re;
}
LL getphi(LL n) {
LL ans = n;
for(LL i = 2; i * i <= n; i++) {
if(n % i == 0) {
ans = ans / i * (i - 1);
while(n % i == 0) n /= i;
}
}
if(n != 1) ans = ans / n * (n - 1);
return ans;
}
LL work(LL n) {
LL ans = 0;
for(LL i = 1; i * i <= n; i++) {
if(n % i == 0) {
(ans += ksm(n, i) * getphi(n / i) % mod) %= mod;
if(i * i != n) (ans += ksm(n, n / i) * getphi(i) % mod) %= mod;
}
}
return ans * ksm(n, mod - 2) % mod;
}
int main() {
for(int T = in(); T --> 0;) printf("%lld
", work(in()));
return 0;
}
以上是关于P4980 模板Polya定理的主要内容,如果未能解决你的问题,请参考以下文章
Luogu4980 模板Polya定理(Polya定理+欧拉函数)