题目大意 :用n种颜色去给圆上正m边形顶点涂色,共有多少种涂色方法(能通过旋转或对称得到的认为是相同的涂色方法)
解题思路:看到这种旋转,对称置换处理的方法数首先考虑ploya定理,即通过每种置换方式保持使着色相同的着色数之和,再求平均。
对于正n边形只考虑旋转,对称就行,其中旋转有n中,对称有n中,再考虑每种置换能保持多少种着色不变。
对于旋转,gcd(n,i) i:0~n-1 代表循环节数,即置换可分解的因子数(请参考组合数学相关知识),着色数ans1 = 颜色数^gcd(n,i)。
对于对称,若n为奇数 循环节数=(n+1)/2 若n为偶数 分对角线过顶点(循环节数=(n-2/2+2))、不过顶点(n/2),着色数相应算出ans2。
最后结果 ans = (ans1+ans2)/(2*n)。
最后注意一点计算过程中可能就超出最大表示范围,最后除以2*n取模就不对了,这里wrong了几次,最后参考网上改了下<pre name="code" class="cpp">(ans*pow(2*m,M-2))%M,有待证明。
#include <iostream>
#include <cstdio>
using namespace std;
#define LL long long
#define M 1000000007
int n,m;
LL pow(LL a,int b){ //a^b
LL ans = 1;
while(b>0){
if(b&1) ans = (ans*a)%M;
a = (a*a)%M;
b>>=1;
}
return ans;
}
int gcd(int a,int b){
if(b==0) return a;
return gcd(b,a%b);
}
int main(){
int T;
cin >> T;
int c = 1;
while(T--){
cin >> n >> m;
LL ans = 0;
//旋转置换
for(int i=0;i<m;i++){
ans = (ans+ pow(n,gcd(m,i)))%M;
}
//对称置换 分奇偶
if(m&1){
ans = (ans+m*pow(n,(m+1)/2))%M;
}
else{
ans = (ans+m/2*pow(n,m/2)*(n+1))%M;
}
// printf("Case #%d: %I64d\n",c++,ans/2/m);
printf("Case #%d: %I64d\n",c++,(ans*pow(2*m,M-2))%M);
}
return 0;
}