設 \(f_i(s)\) 表示 \(s\) 是否有長度爲 \(i\) 的 \(\text{border}\),其取值爲 \(0\) 或 \(1\),不可貴答案爲:c++
若 \(s\) 有長度爲 \(i\) 的 \(\text{border}\),則其有長度爲 \(n-i\) 的週期,所以將 \(f_i(s)\) 的定義改成周期,答案的式子不變。當 \(s\) 有周期 \(i,j\) 時,考慮將必定相同的位置連邊,若獲得 \(cnt\) 個連通塊,則有:git
式子的含義就是連通塊第一個點的字符隨便選,其餘點要和該連通塊第一個點的字符相同。spa
當 \(i+j \leqslant n\) 時,由於有周期 \(i\),造成了模 \(i\) 意義下的剩餘系,因而只需考慮前 \(i\) 個點,由於前 \(i\) 個點都能向後連一條長度爲 \(j\) 的邊,所以獲得 \(\gcd(i,j)\) 個連通塊。當 \(i+j>n\) 時,只有前 \(n-j\) 個點能向後連邊,每連一條邊都有可能使連通塊個數減一,但當連邊造成環時,連通塊個數就不變,得連通塊個數爲 \(i+j-n\) 加上環的個數,不可貴到環的個數爲 \(\max(n-j-(i-\gcd(i,j)),0)\)。code
整理後代入答案的式子得:get
枚舉 \(i+j\) 和 \(\gcd(i,j)\) 得:it
設 \(l=\max(1,\frac{s}{g}-\left\lfloor \frac{n-1}{g}\right\rfloor),r=\min(\left\lfloor\frac{n-1}{g}\right\rfloor,\frac{s}{g}-1)\),反演得:class
注意到:gc
所以直接用推得的式子計算的複雜度爲 \(O(n\log^2n)\)。im
#include<bits/stdc++.h> #define maxn 200010 #define p 1000000007 using namespace std; typedef long long ll; template<typename T> inline void read(T &x) { x=0;char c=getchar();bool flag=false; while(!isdigit(c)){if(c=='-')flag=true;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} if(flag)x=-x; } int n,k,tot; ll ans; int pri[maxn]; ll mu[maxn],pw[maxn]; bool tag[maxn]; vector<int> ve[maxn]; ll inv(ll x) { ll v=1,y=p-2; while(y) { if(y&1) v=v*x%p; x=x*x%p,y>>=1; } return v; } void init(int n) { mu[1]=pw[0]=1; for(int i=1;i<=n;++i) pw[i]=pw[i-1]*k%p; for(int i=2;i<=n;++i) { if(!tag[i]) mu[pri[++tot]=i]=p-1; for(int j=1;j<=tot;++j) { int k=i*pri[j]; if(k>n) break; tag[k]=true; if(i%pri[j]) mu[k]=p-mu[i]; else break; } } for(int i=1;i<=n;++i) for(int j=i;j<=n;j+=i) ve[j].push_back(i); } ll calc(int lim,int sum) { if(lim<=0||sum<=1) return 0; ll l=max(1,sum-lim),r=min(lim,sum-1),v=0; if(l>r) return 0; for(int i=0;i<ve[sum].size();++i) { int d=ve[sum][i]; v=(v+mu[d]*(r/d-(l-1)/d)%p)%p; } return v; } int main() { read(n),read(k),init(2*n); for(int s=2;s<=2*n-2;++s) { for(int i=0;i<ve[s].size();++i) { int g=ve[s][i]; ans=(ans+calc((n-1)/g,s/g)*pw[max(s-n,g)]%p)%p; } } printf("%lld",ans*inv(pw[n])%p); return 0; }