分析:
首先中间的大珠子需要一种颜色,而这种颜色是不能为周围珠子使用的:m=k-1
如果没有“相邻颜色不同”这个限制,这道题就和poj2154一样了
poj2154
旋转i个珠子,轮换个数就是 gcd(n,i) g c d ( n , i )
不动点数: mgcd(n,i) m g c d ( n , i )
ans=1n∑ni=1mgcd(i,n) a n s = 1 n ∑ i = 1 n m g c d ( i , n )∑ni=1gcd(i,n) ∑ i = 1 n g c d ( i , n )
=∑ni=1∑d|n∑d|iphi(d) = ∑ i = 1 n ∑ d | n ∑ d | i p h i ( d )
=∑ni=1∑d|n∑d|iphi(d) = ∑ i = 1 n ∑ d | n ∑ d | i p h i ( d )
=∑d|nndphi(d) = ∑ d | n n d p h i ( d )ans=1n∑ni=1phi(nd)∗md a n s = 1 n ∑ i = 1 n p h i ( n d ) ∗ m d
如果有了相邻不同的限制,相邻的轮换就应该染不同色
显然,只要满足长度
gcd(n,i)
g
c
d
(
n
,
i
)
的一段相邻颜色不同整个环就不同了
(这
gcd(n,i)
g
c
d
(
n
,
i
)
个元素一定属于不同的轮换)
把轮换视为一个点,那我们就对这
gcd(n,i)
g
c
d
(
n
,
i
)
个点染色
设计状态:
f[i]
f
[
i
]
表示给一个
i
i
个点环上色,使得相邻不同色的方案数
转移:
f[1]=m,f[2]=m∗(m−1),f[3]=m∗(m−1)∗(m−2) f [ 1 ] = m , f [ 2 ] = m ∗ ( m − 1 ) , f [ 3 ] = m ∗ ( m − 1 ) ∗ ( m − 2 )
一开始我怎么也不明白这个式子,想了好久:
我们把环从第
i
i
个点劈开,把环变成序列,这样队首就是,
i−1
i
−
1
是队尾
队尾和队首的颜色不能相同,且
i
i
不能与相同,所以
(m−2)∗f[i−1]
(
m
−
2
)
∗
f
[
i
−
1
]
但是
i
i
可以和颜色一样:
(m−1)∗f[i−2]
(
m
−
1
)
∗
f
[
i
−
2
]
对于任意一个置换,不动点的数目:
f(gcd(n,i))
f
(
g
c
d
(
n
,
i
)
)
用矩阵是很容易计算的
ans=kn∑ni=1f(gcd(n,i)) a n s = k n ∑ i = 1 n f ( g c d ( n , i ) )
利用poj2865的奇技淫巧:
ans=kn∑d|nphi(nd)f(d) a n s = k n ∑ d | n p h i ( n d ) f ( d )
注意:这里有一个小限制,当d=1时(即只有一个轮换),上式值为0
tip
dp想不出来。。。一开始只是简单的认为是 m∗(m−1)∗(m−1)∗...∗(m−2) m ∗ ( m − 1 ) ∗ ( m − 1 ) ∗ . . . ∗ ( m − 2 )
至于最后的奇技淫巧,觉得就是计算
f(i)
f
(
i
)
的累积次数,从式子上看:
∑d|nd∗phi(nd)
∑
d
|
n
d
∗
p
h
i
(
n
d
)
转化之后,
phi(nd)
p
h
i
(
n
d
)
是不变的,函数操作加在
d
d
<script type="math/tex" id="MathJax-Element-4050">d</script>上即可
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
using namespace std;
const int N=35000;
const ll p=1e9+7;
ll n,m,k,f1,f2,f3;
int sshu[N],tot=0;
bool no[N];
struct node{
ll H[2][2];
node operator *(const node &a) const
{
node ans;
for (int i=0;i<2;i++)
for (int j=0;j<2;j++)
{
ans.H[i][j]=0;
for (int k=0;k<2;k++)
ans.H[i][j]=(ans.H[i][j]+H[i][k]*a.H[k][j]%p)%p;
}
return ans;
}
node KSM(ll b)
{
node ans=(*this),a=(*this);
b--;
while (b)
{
if (b&1) ans=ans*a;
b>>=1;
a=a*a;
}
return ans;
}
};
node H,A;
void prepare()
{
for (int i=2;i<N;i++)
{
if (!no[i]) sshu[++tot]=i;
for (int j=1;j<=tot&&sshu[j]*i<N;j++)
{
no[sshu[j]*i]=1;
if (i%sshu[j]==0) break;
}
}
}
ll phi(ll x)
{
ll ans=x;
for (int i=1;i<=tot&&sshu[i]*sshu[i]<=x;i++) if (x%sshu[i]==0)
{
ans=ans/sshu[i]*(sshu[i]-1);
while (x%sshu[i]==0) x/=sshu[i];
}
if (x>1) ans=ans/x*(x-1);
return ans;
}
ll KSM(ll a,ll b)
{
ll t=1; a%=p;
while (b)
{
if (b&1) t=(t*a)%p;
b>>=1;
a=(a*a)%p;
}
return t%p;
}
ll F(ll x)
{
if (x==1) return f1;
if (x==2) return f2;
if (x==3) return f3;
A=H.KSM(x-3);
return (A.H[0][0]*f3%p+A.H[1][0]*f2%p)%p;
}
int main()
{
prepare();
while (scanf("%lld%lld",&n,&k)!=EOF)
{
m=k-1;
f1=m%p; f2=m%p*(m-1)%p; f3=m%p*(m-1)%p*(m-2)%p;
H.H[0][0]=m-2; H.H[0][1]=1; H.H[1][0]=m-1; H.H[1][1]=0;
ll ans=0;
for (ll i=1;i*i<=n;i++) if (n%i==0)
{
ll x=i,y=n/i;
if (x!=1) ans=(ans+phi(y)%p*F(x)%p)%p;
if (x!=y) ans=(ans+phi(x)%p*F(y)%p)%p;
}
ans=(ans*k)%p;
ans=(ans%p*KSM(n,p-2)%p)%p;
printf("%lld\n",ans);
}
return 0;
}