题解 P4980 【【模板】Polya定理】
rEdWhitE_uMbrElla
2018-12-09 21:53:05
好题呢,不仅是单纯的Polya定理欸
0. 题目理解
可能是人弱的原因,一开始我把```本质不同的染色方案```理解成不仅要不能通过旋转与别的染色方案相同,还要不能通过翻转与别的染色方案相同,然后样例看不懂。实际上,```本质不同的染色方案```就**只需要不能通过旋转与别的染色方案相同。**
1. 在Polya定理之前——Burnside引理
burnside引理是polya定理的基础。
burnside引理大概是长这样的:
---
对于每个置换f,我们定义C(f)为在置换f下保持不变的方案数。
则有: 本质不同的方案数为C(f)的平均数。
$ans=\frac{1}{\left | G \right |} \sum _{f \in G} C(f)$
---
并不是重点,就不细讲了。
然而Burnside引理需要枚举每个方案,会TLE。
2. Polya定理
我们知道对于每个置换f,我们一定能把它表示成循环的形式。那么我们定义L(f)为f的循环节数,k为颜色数(其实k=n)。根据该定理,则有$k^{L(f)}=C(f)$。那么问题就转化为了求L(f)。
这题很良心,置换只有旋转,于是可以简单解决,否则光是求L(f)就要O(n)了。同时,```|G|=n```首先我们设这是第i次旋转(初始状态算第一次旋转),则有每个循环节长度为```lcm(n,i)/i```,那么```L(f)=n/(lcm(n,i)/i)```,即```L(f)=gcd(n,i)```。所以,$ans=\frac{1}{n} \sum _{f \in G} k^{gcd(n,i)}$
3. 优化
然而我们发现,现在我们求每一个ans就已经是O(n)了,还有一个常数巨大的乘方,,,可能仅仅是每个测试点里的一组数据就超时,,然而我们还有一个t。。。
- 优化1:快速幂
应该都会吧,,不讲了。。。
- 优化2: 欧拉函数
我们可以从gcd优化。设$d_i=gcd(n,i)$。明显,所有d都是n约数。设有x个d是相等的,那么可以枚举n的约数。设枚举到了第j个约数$p_j$,那么$d=n/p_j$。我们也很容易发现$x=\varphi (n/d)$。所以$ans=\frac{1}{n} \sum _{p|n} \varphi (p)\times k^{\frac {n}{p}}$ 于是求ans只要$O(\sqrt n)$了
- 优化3:充分利用```k=n```
其实这是一个主要目的为防止溢出的优化,但同时也对常数有一定优化。
在之前的算法中,我们因为要在算完$k^{L(f)}$的和后除以n,然而对于除法我们无法使用同余,所以不能在求和时取模,那么有很大的溢出风险。然而因为```n=k```,我们完全可以将k的指数减1,同时还解决了要处以n的尴尬,那么求和时完全是可以防止溢出的(为了稳妥起见,我用了```__int128```)。过程如下:
$\because k=n$
$\therefore ans= \sum_{p|n}\frac{\varphi (p)\times k^{\frac {n}{p}}}{n}=\sum_{p|n}\frac{\varphi (p)\times n^{\frac {n}{p}}}{n}=\sum_{p|n}\varphi (p)\times n^{\frac {n}{p}-1}$
好了,上代码:
```cpp
#include<bits/stdc++.h>
using namespace std;
namespace FastIO{ //IO优化
char gc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
template<typename __Type_of__scan>
void scan(__Type_of__scan &x){
__Type_of__scan f=1;x=0;char s=gc();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=gc();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=gc();}
x*=f;
}
char buf[100000],*pp=buf;
void pc(const char c){
if(pp-buf==100000)fwrite(buf,1,100000,stdout),pp=buf;
*pp++=c;
}
template<typename __Type_of__preprint>
void fsh(__Type_of__preprint x){
if(x<0){pc('-');x=-x;}
if(x>9)fsh(x/10);
pc(x%10+'0');
}
template<typename __Type_of__print>
void print(__Type_of__print x){fsh(x);fwrite(buf,1,pp-buf,stdout);pp=buf;}
}
using namespace FastIO;
typedef __int128 LL;
const LL mod=1e9+7;
LL qpow(LL x,LL y){ //快速幂
LL res = 1;
while(y){
if(y&1) res = res*x%mod;
x = x*x%mod;
y >>= 1;
}
return res;
}
LL euler_phi(LL n) { //欧拉函数
LL res=1;
for(LL i=2; i*i<=n; i++)
if(n%i==0) {
n/=i,res=res*(i-1);
while(n%i==0) n/=i,res=res*i;
}
if(n>1) res=res*(n-1);
return res;
}
LL polya(LL m,LL n) { //polya定理主体
LL tot=0;
for(LL i=1; i*i<=n; i++) {
if(n%i) continue;
tot+=euler_phi(i)*qpow(m,n/i-1);
if(i*i!=n) tot+=euler_phi(n/i)*qpow(m,i-1);
}
return tot%mod;
}
int main(){
int t;
scan(t);
for(;t;--t){
LL n;
scan(n),print(polya(n,n)),pc('\n');
}
}
```