一、题目
题目描述
有一个 n n n个点的环,相邻两点连边,有一个中心点向所有点连边,对这个环 k k k染色,要求有边相连的点颜色不同,求旋转后本质不同的方案数。
数据范围
3 ≤ n ≤ 1 e 9 , 4 ≤ k ≤ 1 e 9 3\leq n\leq1e9,4\leq k\leq1e9 3≤n≤1e9,4≤k≤1e9
二、解法
首先中间的点只能是一种和其他点不一样的颜色,先钦定中间的颜色,其他的就变成了 k − 1 k-1 k−1染色,外面套一个 burnside \text{burnside} burnside,有欧拉函数优化计算我就不多说了,考虑算不动点个数(下面 k k k默认 − 1 -1 −1)。
设 f [ i ] f[i] f[i]为 i i i个循环节的方案, f [ 1 ] = 0 f[1]=0 f[1]=0(因为是一整个), f [ 2 ] = k ( k − 1 ) f[2]=k(k-1) f[2]=k(k−1),考虑转移,就是在首尾之间插入一个点,可以插入一个和首尾颜色不同的点,就是 f [ i − 1 ] × ( k − 2 ) f[i-1]\times(k-2) f[i−1]×(k−2),但还要考虑插入点之后原来的首尾颜色可能相同,但是上面的转移却默认了不同,我们强制首尾相同,就从 f [ i − 2 ] f[i-2] f[i−2]处拿方案数,然后这个点可以选 k − 1 k-1 k−1种颜色,就是 f [ i − 2 ] × ( k − 1 ) f[i-2]\times(k-1) f[i−2]×(k−1),容易发现上面的转移很容易套上矩阵加速,优化一下就完了呗。
我又卡了很久常,可以预处理 1 e 6 1e6 1e6以内的 p h i phi phi,还有就是不能乱开 l o n g l o n g long long longlong
#include <cstdio>
#include <cstring>
const int jzm = 1e9+7;
const int M = 1000005;
#define ll long long
int read()
{
int x=0,flag=1;
char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,cnt,vis[M],p[M],ph[M];ll k,ans;
struct Matrix
{
int n,m;ll a[3][3];
Matrix() {n=m=0;memset(a,0,sizeof a);}
Matrix operator * (const Matrix &b) const
{
Matrix r;
r.n=n;r.m=b.m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=b.m;k++)
r.a[i][k]=(r.a[i][k]+a[i][j]*b.a[j][k])%jzm;
return r;
}
}A,F;
void init(int n)
{
ph[1]=1;
for(int i=2;i<=n;i++)
{
if(!vis[i]) p[++cnt]=i,ph[i]=i-1;
for(int j=1;j<=cnt && i*p[j]<=n;j++)
{
vis[i*p[j]]=1;
if(i%p[j]==0)
{
ph[i*p[j]]=ph[i]*p[j];
break;
}
ph[i*p[j]]=ph[i]*(p[j]-1);
}
}
}
Matrix qkpow(Matrix a,int b)
{
Matrix r;
r.n=r.m=a.n;
for(int i=1;i<=r.n;i++)
r.a[i][i]=1;
while(b>0)
{
if(b&1) r=r*a;
a=a*a;
b>>=1;
}
return r;
}
ll fspow(ll a,int b)
{
int r=1;
while(b>0)
{
if(b&1) r=r*a%jzm;
a=a*a%jzm;
b>>=1;
}
return r;
}
int phi(int x)
{
if(x<=1e6) return ph[x];
int r=x;
for(int i=1;p[i]*p[i]<=x;i++)
if(x%p[i]==0)
{
r=r/p[i]*(p[i]-1);
while(x%p[i]==0) x/=p[i];
}
if(x>1) r=r/x*(x-1);
return r%jzm;
}
ll work(int x)
{
if(x==1) return 0;
A.n=A.m=F.n=2;F.m=1;
A.a[1][1]=k-2;A.a[1][2]=k-1;A.a[2][1]=1;
F.a[1][1]=k*(k-1)%jzm;F.a[2][1]=0;
F=qkpow(A,x-2)*F;
return F.a[1][1];
}
signed main()
{
init(1e6);
while(~scanf("%d %lld",&n,&k))
{
k--;ans=0;
for(int i=1;i*i<=n;i++)
if(n%i==0)
{
ans=(ans+phi(i)*work(n/i))%jzm;
if(i*i!=n) ans=(ans+phi(n/i)*work(i))%jzm;
}
ans=ans*(k+1)%jzm*fspow(n,jzm-2)%jzm;
printf("%lld\n",ans);
}
}