Let it Bead POJ 2409
题意:给定k种颜色给长度为n的项链着色,项链可以旋转和翻转,求非等价着色数
思路:Burnside定理,直接套用公式
N
(
G
,
C
)
=
1
∣
G
∣
∑
f
∈
G
C
(
f
)
=
1
∣
G
∣
∑
f
∈
G
k
#
(
f
)
N(G,C)=\frac 1{|G|} \sum_{f\in G}C(f)=\frac 1{|G|}\sum_{f\in G}k^{\#(f)}
N(G,C)=∣G∣1f∈G∑C(f)=∣G∣1f∈G∑k#(f)
分两种情况
(1)旋转:
假设我们旋转x个位置后与原来的着色方案相同,即第i个石头和第i+x个石头的颜色相同。递推下去得到第i个石头与第 i+xt 个石头颜色相同,所以
x
t
≡
0
(
m
o
d
n
)
xt\equiv 0(mod\ n)
xt≡0(mod n),求得最小的 t 是
n
g
c
d
(
x
,
n
)
\frac n{gcd(x,n)}
gcd(x,n)n,
t
t
t 就是一个循环因子的大小,所以有
n
t
\frac nt
tn个循环因子,也就是gcd(x,n)个循环因子,每个循环因子有k种颜色可以选择,因此该情况下总的不变着色数是
∑
C
(
f
)
=
∑
0
≤
i
<
n
k
g
c
d
(
i
,
n
)
\sum C(f)=\sum_{0\le i<n}k^{gcd(i,n)}
∑C(f)=0≤i<n∑kgcd(i,n)
(2)翻转:
按对称轴讨论有多少种选择即可
- 如果n是奇数那么有:对于每一个对称置换 f f f有,顶点单独选择一种颜色,其余点按对称轴对应两点选择一种颜色,因此有 k × k n − 1 2 k\times k^{\frac {n-1}{2}} k×k2n−1个着色保持不变,这样的置换一共有n种,因此总数为 n × k × k n − 1 2 n\times k\times k^{\frac {n-1}{2}} n×k×k2n−1
- 如果n为偶数那么有:如果按顶点对称的,那么两个顶点选择一种颜色,其余点按对称轴对应两点选择一种颜色,有
k
2
×
k
n
−
2
2
k^2\times k^{\frac {n-2}2}
k2×k2n−2,这样的置换有
n
2
\frac n2
2n个,因此有
n
2
×
k
2
×
k
n
−
2
2
\frac n2\times k^2\times k^{\frac {n-2}2}
2n×k2×k2n−2
如果按中线对称,那么就对应两点选择一种颜色,有 k n 2 k^{\frac n2} k2n个着色,这样的置换有 n 2 \frac n2 2n个,因此有 n 2 × k n 2 \frac n2\times k^{\frac n2} 2n×k2n
因此该情况下总的不变着色数为 n 2 × k n 2 + n 2 × k n 2 + 1 \frac n2\times k^{\frac n2}+\frac n2\times k^{\frac {n}2+1} 2n×k2n+2n×k2n+1
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cstring>
#include <string>
#include <cmath>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b) memset(a,b,sizeof(a))
#define mp make_pair
#define ll long long
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define isZero(d) (abs(d) < 1e-8)
using namespace std;
const int maxn=1e5+5,INF=0x3f3f3f3f;
const int mod=1e9+7;
ll n,k;
ll QuickPower(ll base,ll n)
{
ll ret=1;
while(n)
{
if(n&1)
ret=ret*base;
n>>=1;
base=base*base;
}
return ret;
}
ll gcd(ll a,ll b)
{
return b?gcd(b,a%b):a;
}
int main()
{
while(cin>>k>>n&&(n||k))
{
ll ans=0;
for(int i=1;i<=n;++i)
ans+=QuickPower(k,gcd(n,i));
if(n&1)
ans+=QuickPower(k,(n+1)/2)*n;
else
ans+=QuickPower(k,n/2+1)*n/2+QuickPower(k,n/2)*n/2;
cout<<ans/(2*n)<<"\n";
}
return 0;
}
Invoker HDU - 3923
题意:用k个字母组成长度为n的单词,旋转翻转算同一种,问有多少种不同的单词
思路:和上题一样。唯一不同的是答案需要取余,最后除以2n的时候,需要算一下2n的逆元
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cstring>
#include <string>
#include <cmath>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b) memset(a,b,sizeof(a))
#define mp make_pair
#define ll long long
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define isZero(d) (abs(d) < 1e-8)
using namespace std;
const int maxn=1e5+5,INF=0x3f3f3f3f;
const int mod=1e9+7;
ll n,k;
ll QuickPower(ll base,ll n)
{
ll ret=1;
while(n)
{
if(n&1)
ret=ret*base%mod;
n>>=1;
base=base*base%mod;
}
return ret;
}
ll gcd(ll a,ll b)
{
return b?gcd(b,a%b):a;
}
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1,y=0;
return a;
}
ll q=exgcd(b,a%b,y,x);
y-=a/b*x;
return q;
}
ll inverse(ll a,ll mod)
{
ll x,y;
exgcd(a,mod,x,y);
return (x%mod+mod)%mod;
}
int main()
{
int T;
cin>>T;
int Cas=0;
while(T--)
{
cin>>k>>n;
ll ans=0;
for(int i=1;i<=n;++i)
ans=(ans+QuickPower(k,gcd(n,i)))%mod;
if(n&1)
ans=(ans+QuickPower(k,(n+1)/2)*n)%mod;
else
ans=(ans+QuickPower(k,n/2+1)*n/2+QuickPower(k,n/2)*n/2)%mod;
cout<<"Case #"<<++Cas<<": "<<ans*inverse(2*n,mod)%mod<<"\n";
}
return 0;
}
Color POJ - 2154
题意:用n种颜色给长度为n的项链着色,只能旋转,不能翻转,求非等价着色数
思路:只需要考虑旋转的情况,即
N
(
G
,
C
)
=
1
∣
G
∣
∑
f
∈
G
k
#
(
f
)
=
1
n
∑
0
≤
i
<
n
n
g
c
d
(
i
,
n
)
N(G,C)=\frac 1{|G|}\sum_{f\in G}k^{\#(f)}=\frac 1n\sum_{0\le i <n} n^{gcd(i,n)}
N(G,C)=∣G∣1f∈G∑k#(f)=n10≤i<n∑ngcd(i,n)
直接计算复杂度太高会超时,考虑合并最大公约数相同的项
N
(
G
,
C
)
=
1
n
∑
0
≤
i
<
n
n
g
c
d
(
i
,
n
)
N(G,C)=\frac 1n\sum_{0\le i <n} n^{gcd(i,n)}
N(G,C)=n1∑0≤i<nngcd(i,n)
=
1
n
∑
d
∣
n
n
d
∑
0
≤
i
<
n
[
g
c
d
(
i
,
n
)
=
d
]
=\frac 1n \sum_{d|n}n^d\sum_{0\le i<n}[gcd(i,n)=d]
=n1∑d∣nnd∑0≤i<n[gcd(i,n)=d]
=
1
n
∑
d
∣
n
n
d
∑
0
≤
i
<
n
[
g
c
d
(
i
d
,
n
d
)
=
1
]
=\frac 1n \sum_{d|n}n^d\sum_{0\le i<n}[gcd(\frac id,\frac nd)=1]
=n1∑d∣nnd∑0≤i<n[gcd(di,dn)=1]
=
1
n
∑
d
∣
n
n
d
∑
0
≤
t
<
n
d
[
g
c
d
(
t
,
n
d
)
=
1
]
=\frac 1n \sum_{d|n}n^d\sum_{0\le t<\frac nd}[gcd(t,\frac nd)=1]
=n1∑d∣nnd∑0≤t<dn[gcd(t,dn)=1]
=
∑
d
∣
n
n
d
−
1
φ
(
n
d
)
= \sum_{d|n}n^{d-1} \varphi(\frac nd)
=∑d∣nnd−1φ(dn)
因此我们枚举 n 的因子即可
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cstring>
#include <string>
#include <cmath>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b) memset(a,b,sizeof(a))
#define mp make_pair
#define ll long long
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define isZero(d) (abs(d) < 1e-8)
using namespace std;
const int maxn=1e5+5,INF=0x3f3f3f3f;
const int mod=1e9+7;
int T,N,P;
int visit[maxn],prime[maxn],cnt;
void Prime(int N)
{
mes(visit,0);
mes(prime,0);
cnt=0;
for(int i=2;i<N;++i)
{
if(!visit[i])
prime[++cnt]=i;
for(int j=1;j<=cnt&&i*prime[j]<N;++j)
{
visit[i*prime[j]]=1;
if(i%prime[j]==0)
break;
}
}
}
ll euler(ll n)
{
ll ret=n,a=n;
for(ll i=1;prime[i]*prime[i]<=a;++i)
{
if(a%prime[i]==0)
ret=ret/prime[i]*(prime[i]-1);
while(a%prime[i]==0)
a/=prime[i];
}
if(a>1)
ret=ret/a*(a-1);
return ret;
}
ll QuickPower(ll base,ll n,ll mod=1e9+7)
{
ll ret=1;
while(n)
{
if(n&1)
ret=(ret*base)%mod;
n>>=1;
base=(base*base)%mod;
}
return ret;
}
int main()
{
Prime(maxn);
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&N,&P);
ll ans=0;
for(int i=1;i*i<=N;++i)
{
if(i*i==N)
ans=(ans+QuickPower(N,i-1,P)*euler(i))%P;
else if(N%i==0)
ans=(ans+QuickPower(N,i-1,P)*euler(N/i)+QuickPower(N,N/i-1,P)*euler(i))%P;
}
printf("%lld\n",ans);
}
return 0;
}