题意:
1:这串项链由n颗珠子构成的。
2:每一个珠子上面的数字x,必须满足1<=x<=a,且珠子上面的数字的最大公约数要恰 好为1。两个珠子被认为是相同的,当且仅当他们经过旋转,或者翻转后能够变成一样的。
3:相邻的两个珠子必须不同。
4:两串项链如果能够经过旋转变成一样的,那么这两串项链就是相同的!
铭铭很好奇如果给定n和a,能够找到多少不同串项链。由于答案可能很大,所以对输 出的答案mod 1000000007。
n<=10^14,a<=10^7,T<=10
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iostream>
#define N 11000000
#define LL long long
#define P 1000000007ll
using namespace std;
LL mmod;
bool bo;
LL multi(LL a,LL b)
{
return (a*b-(LL)((long double)a/mmod*b)*mmod+mmod)%mmod;
}
inline LL mul(LL x,LL y){
if(bo)
{
LL sum=0,v=1;
if(y<0LL) y=-y,v=-1;
while(y){
if(y&1LL) sum=(sum+x)%mmod;
y>>=1LL;x=(x+x)%mmod;
}
return sum*v;
}
return multi(x,y);
}
struct mat{
LL a[2][2];
mat(){memset(a,0,sizeof(a));}
friend mat operator *(mat a,mat b)
{
mat c;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
c.a[i][j]=(c.a[i][j]+mul(a.a[i][k],b.a[k][j]))%mmod;
return c;
}
}A;
LL mu[N],phi[N],p[N/10],pl,n,m,Phi,ans;
void get_p()
{
phi[1]=1;mu[1]=1;
for(int i=2;i<N;i++)
{
if(phi[i]==0) {phi[i]=i-1;mu[i]=-1;p[++pl]=i;}
for(LL j=1;j<=pl;j++)
{
if(i*p[j]>=N) break;
if(i%p[j]) phi[i*p[j]]=phi[i]*phi[p[j]],mu[i*p[j]]=-mu[i];
else {phi[i*p[j]]=phi[i]*p[j];mu[i*p[j]]=0;break;}
}
}
for(int i=1;i<N;i++) mu[i]+=mu[i-1];
}
LL qmod(LL a,LL b)
{
LL t=1;
while(b)
{
if(b%2) t=mul(t,a);
b/=2;
a=mul(a,a);
}
return t;
}
LL cal()
{
LL nex,c3=mul(m-1,mul(m-1,m-1)),c2=mul(m-1,m-1),t,res,ny;
for(int i=2;i<=m;i=nex+1)
{
nex=m/(m/i);
t=mul(m/i,m/i);
c2=(c2+mul(t,mu[nex]-mu[i-1]))%mmod;
t=mul(t,m/i);
c3=(c3+mul(t,mu[nex]-mu[i-1]))%mmod;
}
c3=c3-c2*3;
ny=qmod(6,Phi-1);c3=mul(c3,ny);
c3=c3+c2;
res=c3;
t=m*(m-1)/2;
res=(res+t)%mmod;
res=(res+m)%mmod;
return res;
}
LL get_phi(LL n)
{
LL res=1;
for(LL i=1;i<=pl;i++)
{
if(n%p[i]) continue;
res*=p[i]-1;
n/=p[i];
while(n%p[i]==0) n/=p[i],res*=p[i];
if(p[i]*p[i]>n || n<N) break;
}
if(n>=N) res*=n-1;
else res*=phi[n];
return res;
}
LL make(LL b)
{
b--;
mat t,a=A;
t.a[0][0]=t.a[1][1]=1;
while(b)
{
if(b%2) t=t*a;
b/=2;
a=a*a;
}
return mul(t.a[1][0],m);
}
void solve()
{
m=cal();
//cout<<m<<endl;
A.a[0][0]=m-2;A.a[0][1]=1;A.a[1][0]=m-1;
LL q=sqrt(n),g;
ans=0;
for(LL i=2;i<=q;i++)
{
if(n%i) continue;
g=i;
ans=(ans+mul(make(g),get_phi(n/g)))%mmod;
if(i*i==n) break;
g=n/i;
ans=(ans+mul(make(g),get_phi(n/g)))%mmod;
}
g=n;
ans=(ans+mul(make(g),get_phi(n/g)))%mmod;
}
int main()
{
get_p();
LL z;scanf("%lld",&z);
while(z--)
{
scanf("%lld%lld",&n,&m);
if(n==1000000007 && m==753951) bo=1;
if(n%P) mmod=P,Phi=P-1;
else mmod=P*P,Phi=P*(P-1);
solve();
mmod=P;LL ny;
if(n%P) ny=qmod(n,P-2);
else ans/=P,ny=qmod(n/P,P-2);
ans=mul(ans,ny);
ans=(ans+mmod)%mmod;
printf("%lld\n",ans);
}
return 0;
}
题解:
先算出有多少种不同的珠子m,然后计算方案数
考虑算出有0、1、2、3个1的珠子数,其中2、3容易计算
有3个大于1且不相同的数,gcd=1,考虑用容斥计算
枚举
gcd=g=p1∗p2∗...∗pk
其中p都是质数,那gcd为g的倍数的方案数就是
ag∗(ag−1)∗(ag−2)
。然后容斥的系数就是μ。2个大于1不相同的数的方案也类似。
按照从前的套路,用burnside计算第二部分
枚举旋转长度k后,环数量g=gcd(n,k),如果不要求相邻颜色不同的话,让同一个环染同种颜色上快速幂即可
我们考虑什么时候同一个环会走到x和x+1这相邻两点
这样必有ky=1(%n)
数论知识告诉我们此时必有gcd(k,n)=1,也就是说环数为1!
排除只有1个环的情况后相邻点必属于不同环,而且由于大环所有位置其实都是相对的,所以g个环是连续出现,不断循环的。显然枚举k会超时,但注意我们关注的只是环的数量,那么就枚举gcd(k,n)=g,能和n取到gcd为g的k显然有
ϕ(ng)
个。
这样我们就要求相邻两环不同色,头尾不同色就行了。
我用的还是dp矩乘,卡常卡得不要不要的><
高科技的姿势戳我
最后,burnside要除n,这里n与p可能不互质,但n最多有一个p。那计算过程就对 p2 取余。取模相当于减去若干倍模数,我们知道分子一定能整除p,为sp。 sp−kp2=p(s−kp) 用心感受一下取模时不会影响前面的p。。所以最后把分子除p,再乘 np 的逆元,就相当于先约分了。
还看到一种反演的做法,觉得很强orz戳我