题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3202
解法:读完题目,很明显我们有两个任务:(1)计算符合提交的颜色数目;(2)计算有m种颜色,长度为n相邻两个颜色不相同的本质不同项链数目(旋转相同被认为是本质相同。)
Part 1:
我们设c2,c3分别为gcd=1的有序整数对以及有序三元组的数目。
那么我们可以得出答案即为m=(c3-(c2-1)*3-1)/6+c2。
计算c2,c3需要莫比乌斯反演的知识,这里直接给出公式好了(不会的同学可以参考《具体数学》)
c2=simga(miu(i)*floor(a/i)^2),c3=simga(miu(i)*floor(a/i)^3),1<=i<=a。
Part 2:
我们设上面所求答案m。
接下来需要用到polya计数相关知识。
我们设f(i)表示长度为i的项链,相邻颜色两两不同的方案数。
那么答案就是sigma(f(gcd(i,n)),1<=i<=n)/n=sigma(phi(d)*f(n/d),d|n)/n。
如何求f(i)?
若第i-1个和第1个的颜色不同,情况数有f(i-1),那么第i个可以放m-2种颜色,方案数为f(i-1)*(m-2)
若第i-1个和第1个的颜色相同,那么i-2肯定和第1个不同,情况数有f(i-2),第i个可以放m-1种颜色。
所以f(i)=f(i-1)*(m-2)+f(i-2)*(m-1)。于是就可以在log(n)的时间求出f(n)了(我的程序里是求过通项的)。
于是这题差不多解决了= =。我们可以通过官方数据了。。
但是。。。vfk大神在bzoj上加了一组n%mod!=0的数据。。这样我们到最后就不能直接乘逆元了。。
于是对于这种情况我们先对答案%mod^2,然后这个结果必然被mod整除,于是我们直接除掉,再乘上(n/mod)的逆元。(n<mod^2,所以这下n/mod的逆元存在了。。)
下面是代码(在bzoj上目前rank1)
/************************************************************** Problem: 3202 User: hta Language: C++ Result: Accepted Time:4636 ms Memory:93072 kb ****************************************************************/ #include <iostream> #include <cstring> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> #include <string> using namespace std; typedef long long LL; const int maxn=10000003; const int mod=1000000007; int A[11],miu[maxn],T,a,sum[maxn]; bool check[maxn]; int totp,prime[maxn/10]; LL N[11],p; inline int get() { int f=0,v=0;char ch; while(!isdigit(ch=getchar()))if(ch=='-')break; if(ch=='-')f=1;else v=ch-48; while(isdigit(ch=getchar()))v=v*10+ch-48; if(f==1)return -v;else return v; } void init() { T=get();int n=0; for(int i=1;i<=T;i++)cin>>N[i],A[i]=get(),n=max(n,A[i]); miu[1]=sum[1]=1; for(int i=2;i<=n;i++) { if(!check[i])prime[++totp]=i,miu[i]=-1; for(int j=1,tp;(tp=prime[j]*i)<=n&&j<=totp;j++) { check[tp]=1; if(i%prime[j]==0){miu[tp]=0;break;}else miu[tp]=-miu[i]; } } for(int i=2;i<=n;i++)sum[i]=sum[i-1]+miu[i]; } /*LL Mul(LL a,LL b) { LL res=0;a%=p,b%=p; if(b<0)b+=p; for(;b>0;b/=2,a+=a,a-=(a>=p)*p) if(b%2==1)res+=a,res-=(res>=p)*p; return res; }*/ LL Mul(LL a,LL b) { return (a*b-(LL)((long double)a/p*b)*p+p)%p; } void extgcd(LL a,LL b,LL &x,LL &y) { if(b==0){x=1,y=0;return;} extgcd(b,a%b,x,y); LL tp=x;x=y;y=tp-a/b*x; } LL Pow(LL a,LL b) { LL res=1; for(a%=mod;b>0;b/=2,a=a*a%mod) if(b&1)res=res*a%mod; return res; } LL calc(int a) { int i=1,j,tp; LL c2=0,c3=0; while(i<=a) { j=a/(a/i),tp=a/i; LL tmp=LL(tp)*tp%mod*(sum[j]-sum[i-1])%mod; c2=(c2+tmp)%mod,c3+=tmp*tp,c3%=mod; i=j+1; } LL res=(c3-(c2-1)*3-1)%mod*Pow(6,mod-2)+c2; res%=mod;if(res<0)res+=mod; return res; } LL phi(LL n) { LL res=n; for(LL i=2;i*i<=n;i++) { if(n%i!=0)continue; res-=res/i; while(n%i==0)n/=i; } if(n>1)res-=res/n; return res; } LL f(LL n,LL m) { LL res; if(n%2==1)res=(Pow(m-1,n)-m+1+mod)%mod; else res=(Pow(m-1,n)+m-1+mod)%mod; return res; } LL Pow2(LL a,LL b) { LL res=1; for(a%=p;b>0;b/=2,a=Mul(a,a)) if(b&1)res=Mul(res,a); return res; } LL calc2(int a) { int i=1,j; LL c2=0,c3=0,tp; while(i<=a) { j=a/(a/i),tp=a/i; LL tmp=Mul(tp*tp%p,sum[j]-sum[i-1]); c2=(c2+tmp)%p,c3+=Mul(tmp,tp),c3%=p; i=j+1; } LL x,y; extgcd(6,p,x,y);if(x<0)x+=p; LL res=Mul((c3-(c2-1)*3-1)%p,x)+c2; res%=p;if(res<0)res+=p; return res; } LL f2(LL n,LL m) { LL res; if(n%2==1)res=(Pow2(m-1,n)-m+1+p)%p; else res=(Pow2(m-1,n)+m-1+p)%p; return res; } void work(LL n,int a) { if(n%mod==0) { p=LL(mod)*mod; LL col=calc2(a); LL ans=0; for(LL i=1;i*i<=n;i++) { if(n%i!=0)continue; ans+=Mul(phi(i)%p,f2(n/i,col)); if(i*i!=n)ans+=Mul(phi(n/i)%p,f2(i,col)); ans%=p; } ans/=mod;ans%=mod; LL x,y;extgcd(n/mod,p,x,y); x%=mod;if(x<0)x+=mod; printf("%d\n",int(ans*x%mod)); return; } LL col=calc(a); LL ans=0; for(LL i=1;i*i<=n;i++) { if(n%i!=0)continue; ans+=phi(i)%mod*f(n/i,col); if(i*i!=n)ans+=phi(n/i)%mod*f(i,col); ans%=mod; } printf("%d\n",int(ans*Pow(n,mod-2)%mod)); } int main() { init(); for(int i=1;i<=T;i++)work(N[i],A[i]); return 0; }