标签:计算 rod code 忽略 printf href 入门 c++ ==
\(这篇blog重点讨论Polya的应用, 更详细的证明请百度 .\)
\[L=\frac{1}{|G|}\sum_{i=1}^{|G|}D(a_i)\]
\(L\): 本质不同的方案数.
\(G\): 置换群集合.
\(a_i\): 置换群中的第 \(i\) 个置换.
\(D(a_i)\): 进行 \(a_i\) 这个置换, 状态不会变化的方案 数量.
该引理与下方内容没有太大关系, 可以暂时忽略.
\(Problem\) 链接
有 \(N\) 个石子围成一圈, 使用 \(M\) 种颜色染色, 求有多少种本质不同的方案.
\[L=\frac{1}{|G|}\sum_{i=1}^{|G|}M^{C(g_i)}\]
先丢出公式.
这道题的 置换群 \(G\): 转\(0\)次, 转\(1\)次 ...转 \(N-1\) 次, (皆为顺时针转动).
若满足旋转 \(k\) 个位置, 状态和原来相同, 那么 \(i\) 位置颜色 等于 \((i+k)\%N\) 位置颜色,
旋转 \(t\) 次, 仍和原来位置相同, 即 \(i\) 位置与 \((i+t*k)\%N\) 位置颜色相同,
则 \(i\) 旋转 \(t\) 个 \(k\) 次, 一定能回到原来位置, 即可以无限旋转,
所以 \(i\) 就与 所有 \((i+t*k)\%N\) 位置呈现一个封闭的 \(\color{red}{循环节}\).
设转了 \(t\) 次后, 第一次回到 \(i\) 位置, 则
\(t * k=lcm(k, N)=\frac{k*N}{gcd(k,N)}\),
\(\therefore t=\frac{N}{gcd(k,N)}\),
\(t\) 为\(\color{red}{循环节}\)长度, 则\(\color{red}{循环节}\)数量为 \(\frac{N}{t}=gcd(k,N)\).
公式中 颜色的数量为 \(M\); 在 \(g_i\) 置换下, 有 \(C(g_i)\) 个 \(\color{red}{循环节}\).
此题中 \(C(g_i)=gcd(k,N)\), \[\therefore Ans=L=\frac{1}{N}\sum_{k=0}^{N-1}M^{gcd(k,N)}\]
假如上题增加 \(\color{red}{对称同构}\), 则意味着 原先的两个不同方案 对称 时计为一个方案,
按 \(N\) 的奇偶 分类讨论:
\(N\) 为偶数, 对称轴上可以没有点, 也可以有两个点, 两种对称轴都有 \(\frac{N}{2}\) 种, 总共有 \(N\) 个对称轴置换,
对称轴上无点时, 循环节个数为\(\frac{N}{2}\), 对答案贡献为 \(\frac{M^{\frac{N}{2}}}{2N}\),总贡献为\(\frac{\frac{N}{2}*M^{\frac{N}{2}}}{2N}\).
对称轴上有点时, 循环节个数为\(\frac{N}{2}+1\), 对答案贡献为 \(\frac{M^{\frac{N}{2}+1}}{2N}\), 总贡献为 \(\frac{\frac{N}{2}*M^{\frac{N}{2}+1}}{2N}\).
所以最后的答案为
\[Ans=L=\frac{1}{2N}(\sum_{k=0}^{N-1}M^{gcd(k,N)}+\frac{N}{2}*M^{\frac{N}{2}}+\frac{N}{2}*M^{\frac{N}{2}+1})\]
若 \(\color{red}{N<=10^9}\) , \(O(N)\)计算下式会 \(TLE\), 需要更快的办法,
\[Ans=L=\frac{1}{N}\sum_{k=0}^{N-1}M^{gcd(k,N)}\]
由于 \(gcd(k,N)|N\), 而\(N\)的约数不会超过 \(2\sqrt{N}\) 个,
考虑枚举 \(d\), \((d|N)\)
则就只需统计 \(gcd(k,N)=d\) 的 \(k\) 的个数.
\(d=gcd(k,N)=gcd(d*t, d*\frac{N}{d})\)
则 \(t\) 与 \(\frac{N}{d}\) 互质, 即 \(\color{red}{gcd(t, \frac{N}{d}) = 1}\).
\(\therefore \color{red}{\varphi(\frac{N}{d})}\) 即为 \(gcd(k,N)=d\) 的 \(k\) 的个数.
于是 \[Ans=L=\frac{1}{N}\sum_{d|N}\varphi(\frac{N}{d}) *M^d\]
.
\(如何求解 \varphi(x)?\)
根据定义: \[\varphi(x)=x\prod_{p_i|x}(1-\frac{1}{p_i})\]
通分得: \(\varphi(x) = x\prod_{p_i|x}\frac{p_i-1}{p_i}\)
按上式实现, 时间复杂度小于 \(O(\sqrt{N})\), 均摊 \(O(logN)?\)
这里给出求解函数,
int Get_phi(int x){
int s = x;
for(int i = 2; i*i <= x; i ++)
if(x % i == 0){ //找到一个质因数
s = s/i*(i-1);
while(x%i == 0) x /= i;
}
if(x > 1) s = s/x*(x-1); //不可能出现两个大于 sqrt(N) 的质因数, 所以只可能剩下一个, 处理掉就好 .
return s;
}
所以 \(O(\sqrt{N}),O(logN)\) 分别求出所有约数 \(d\) 和 \(\varphi(\frac{N}{d})\) 即可, 时间复杂度 \(O(\sqrt{N}logN)\).
\(\mathcal{Code}\)
#include<bits/stdc++.h>
#define reg register
const int mod = 1e9 + 7;
int T;
int N;
int phi(int x){
int s = x;
for(reg int i = 2; i*i <= x; i ++)
if(x % i == 0){
s = s/i * (i-1);
while(x % i == 0) x /= i;
}
if(x > 1) s = s/x * (x-1);
return s;
}
int KSM(int a, int b){
int s = 1; a %= mod;
while(b){
if(b & 1) s = 1ll*s*a % mod;
a = 1ll*a*a % mod, b >>= 1;
}
return s;
}
void Work(){
scanf("%d", &N);
int Ans = 0;
int lim = sqrt(N);
for(reg int d = 1; d <= lim; d ++){
if(N % d) continue ;
Ans = ( 1ll*Ans + (1ll*phi(N/d) * KSM(N, d) % mod) ) % mod;
int d_2 = N / d;
if(d_2 == d) continue ;
Ans = ( 1ll*Ans + (1ll*phi(N/d_2) * KSM(N, d_2) % mod) ) % mod;
}
printf("%d\n", (1ll*Ans*KSM(N, mod-2)) % mod);
}
int main(){
scanf("%d", &T);
while(T --) Work();
return 0;
}
更多例题请戳 这里 .
Polya 定理入门[Burnside引理,Polya定理,欧拉函数]
标签:计算 rod code 忽略 printf href 入门 c++ ==
原文地址:https://www.cnblogs.com/zbr162/p/11425079.html