洛谷传送门
BZOJ传送门
解析:
首先稍有常识的人都知道这道题绝对不可能是字符串题。
这种只给串长和字符集大小的题目只可能是计数。
而计数方式有很多啊,DP,群论,生成函数,甚至这道题的做法,莫比乌斯反演。
看数据 D P DP DP和群论基本上就告别正解了,生成函数推不出通项公式也是白搭(而且字符串轮换循环的情况让生成函数也告别正解了)。
这TM谁一眼看得出是莫反啊。
我们先忘记我们知道这道题需要用莫比乌斯反演这件事,来尝试推理一遍。
什么?你不会莫比乌斯反演?敢来淦反演毒瘤省SDOI的题你说你不会莫比乌斯反演?
没办法了,洗洗睡吧
推♂倒:
我们需要求有多少个长度为 n n n,字符集大小为 k k k的字符串的某一个轮换是一个回文串。
首先,字符集大小为 k k k,长度为 n n n的总回文串个数,很显然是 k ⌊ n + 1 2 ⌋ k^{\lfloor\frac{n+1}{2}\rfloor} k⌊2n+1⌋
但是我们也很清楚,并不是所有回文串的每一个轮换都不同,比如 a a a a a aaaaa aaaaa就只能算作一个串。
考虑每个回文串有多少种不同的轮换,答案显然就是最小循环节长度。
但是每个回文串的所有轮换都要考虑一次吗?
我们发现 a b b a a b b a abbaabba abbaabba的所有轮换和 b a a b b a a b baabbaab baabbaab都相同。。。
也就是说,可能出现某个回文串的某个轮换是另一个不同的回文串。
日,感觉分析不下去了。
但是仔细一看,这种情况只有当回文串的循环节长度为偶数的时候才会发生。就是循环节转到一半的情况。
而且只可能是两个回文串平摊贡献,不然循环节长度还能再小。
换句话说,如果考虑所有回文串的不同轮换对答案的贡献,设某个回文串的最小循环节长度为 d d d,则设贡献函数 h ( d ) = ( d % 2 = = 0 ) ? d / 2 : d h(d)=(d\%2==0)?d/2:d h(d)=(d%2==0)?d/2:d
现在令 f ( d ) f(d) f(d)表示所有最小循环节长度为 d d d的回文串个数,则答案为 A n s = ∑ d ∣ n h ( d ) f ( d ) Ans=\sum_{d\mid n}h(d)f(d) Ans=d∣n∑h(d)f(d)
那么现在问题变成了求 f f f。
注意这里要求是最小循环节,即不能有更小的循环节了。
日,感觉分析不下去了。
但是你回去想一想为什么我们会推到最小循环节就会发现:
g ( N ) = ∑ d ∣ N f ( d ) = k ⌊ N + 1 2 ⌋ g(N)=\sum_{d\mid N}f(d)=k^{\lfloor\frac{N+1}{2}\rfloor} g(N)=d∣N∑f(d)=k⌊2N+1⌋
其中 g ( N ) g(N) g(N)表示具有长度为 N N N的循环节的回文串总数。
你知道我要说什么吧
莫比乌斯反演!
f ( N ) = ∑ d ∣ N μ ( d ) g ( N d ) f(N)=\sum_{d\mid N}\mu(d)g(\frac{N}d) f(N)=d∣N∑μ(d)g(dN)
代回去得到:
A n s = ∑ d ∣ n h ( d ) ∑ t ∣ d μ ( d ) g ( d t ) = ∑ d ∣ n g ( d ) ∑ t d ∣ n h ( t d ) μ ( t ) \begin{aligned} Ans&=&&\sum_{d\mid n}h(d)\sum_{t\mid d}\mu(d)g(\frac{d}t)\\ &=&&\sum_{d\mid n}g(d)\sum_{td\mid n}h(td)\mu(t) \end{aligned} Ans==d∣n∑h(d)t∣d∑μ(d)g(td)d∣n∑g(d)td∣n∑h(td)μ(t)
我们现在想把 h ( t d ) h(td) h(td)中的一个常数 d d d提出来得到 t h ( d ) th(d) th(d),因为 ∑ t ∣ n d t μ ( t ) \sum_{t\mid\frac{n}d}t\mu(t) ∑t∣dntμ(t)有一个很好用的性质,一会儿会用到。
现在考虑这个式子 h ( t d ) = t h ( d ) h(td)=th(d) h(td)=th(d)什么时候不成立
很显然就是 t t t为偶数但是 d d d为奇数数的时候。但是这时候可以把 d d d提出来。
考虑这时候这个式子的值是什么: d ∑ t ∣ n d h ( t ) μ ( t ) d\sum_{t\mid \frac{n}{d}}h(t)\mu(t) dt∣dn∑h(t)μ(t)
我们发现对于所有的偶数 t t t,如果 μ ( t ) ! = 0 \mu(t)!=0 μ(t)!=0,都有 h ( t ) μ ( t ) = − h ( t 2 ) μ ( t 2 ) h(t)\mu(t)=-h(\frac{t}2)\mu(\frac{t}2) h(t)μ(t)=−h(2t)μ(2t)。
换句话说, ∑ \sum ∑里面的所有奇数和偶数凑成一对后和为 0 0 0。
特判掉所有这种情况就行了。
去掉上面的情况后我们直接来提出
t
t
t:
A
n
s
=
∑
d
∣
n
g
(
d
)
h
(
d
)
∑
t
∣
n
d
t
μ
(
t
)
Ans=\sum_{d\mid n}g(d)h(d)\sum_{t\mid \frac{n}d}t\mu(t)
Ans=d∣n∑g(d)h(d)t∣dn∑tμ(t)
设 S ( n ) = ∑ t ∣ n t μ ( t ) S(n)=\sum_{t\mid n}t\mu(t) S(n)=∑t∣ntμ(t),这个函数有显然的递推式,设 n n n的唯一分解为 n = ∏ i = 1 t p i k i n=\prod\limits_{i=1}^tp_i^{k_i} n=i=1∏tpiki,则 S ( n ) = ∏ i = 1 n ( 1 − p i ) S(n)=\prod\limits_{i=1}^n(1-p_i) S(n)=i=1∏n(1−pi)
利用Pollard-Rho筛出所有质因子,dfs找出所有因数和 S S S函数值就行了。
代码(很丑,丑到我都不想看):
#include<bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const
inline ll mul(cs ll &a,cs ll &b,cs ll &mod){
return (a*b-(ll)((long double)a/mod*b)*mod+mod)%mod;
}
inline ll quickpow(ll a,ll b,cs ll &mod){
ll res=1;
while(b){
if(b&1)res=mul(res,a,mod);
a=mul(a,a,mod);
b>>=1;
}
return res;
}
inline ll gcd(ll a,ll b){
static ll tmp;
while(b){
tmp=a%b;
a=b;
b=tmp;
}
return a;
}
cs int P=1e7+7;
int prime[P],pcnt,minpri[P];
bool mark[P];
inline void linear_sieves(int len=P-7){
mark[1]=true;
for(int re i=2;i<=len;++i){
if(!mark[i])prime[++pcnt]=i,minpri[i]=i;
for(int re j=1;i*prime[j]<=len;++j){
mark[i*prime[j]]=true;
minpri[i*prime[j]]=minpri[i];
if(i%prime[j]==0)break;
}
}
}
inline bool isprime(ll x){
if(x<=P-7)return !mark[x];
ll t=x-1,s=0;
while(!(t&1))t>>=1,++s;
for(int re i=1;i<=5;++i){
ll p=prime[rand()%pcnt+1]%x;
ll num=quickpow(p,t,x),pre=num;
if(x%p==0)return false;
for(int re j=0;j<s;++j){
num=mul(num,num,x);
if(num==1&&pre!=1&&pre!=x-1)return false;
pre=num;
}
if(num!=1)return false;
}
return true;
}
inline ll Pollard_Rho(ll x){
if(x%2==0)return 2;
if(x%3==0)return 3;
if(x%5==0)return 5;
if(x%7==0)return 7;
ll n=0,m=0,q=1,t=1,c=rand()%(x-1)+1;
for(ll re k=2;;k<<=1,m=n,q=1){
for(ll re i=1;i<=k;++i){
n=(mul(n,n,x)+c)%x;
q=mul(q,abs(n-m),x);
}
if((t=gcd(q,x))>1)return t;
}
}
ll pfact[60];
int pfcnt;
inline void sieve(ll x){
if(x==1)return ;
if(x<=P-7){
while(x>1){
pfact[++pfcnt]=minpri[x];
int p=minpri[x];
while(x%p==0)x/=p;
}
return ;
}
if(isprime(x)){
pfact[++pfcnt]=x;
return ;
}
ll p=x;
while(p==x)p=Pollard_Rho(p);
sieve(p);
while(x%p==0)x/=p;
sieve(x);
}
ll n,k,mod;
ll fact[P];
int fcnt;
tr1::unordered_map<ll,ll>f;
inline ll quickpow(ll a,ll b){ll res=1;while(b){if(b&1)res=mul(res,a,mod);a=mul(a,a,mod);b>>=1;}return res;}
inline ll mul(cs ll &a,cs ll &b){return (a*b-(ll)((long double)a/mod*b)*mod+mod)%mod;}
inline void dfs(ll now,ll nowf,int pos){
if(pos>pfcnt){
fact[++fcnt]=now,f[now]=nowf;
return ;
}
dfs(now,nowf,pos+1);
for(ll p=pfact[pos];;p*=pfact[pos]){
dfs(now*p,mul(nowf,(mod-pfact[pos]%mod+1)%mod),pos+1);
if((n/p)%pfact[pos])return ;
}
}
inline void solve(){
scanf("%lld%lld%lld",&n,&k,&mod);k%=mod;
ll ans=0;
sieve(n);
sort(pfact+1,pfact+pfcnt+1);
pfcnt=unique(pfact+1,pfact+pfcnt+1)-pfact-1;
dfs(1,1,1);
for(int re i=1;i<=fcnt;++i){
ll d=fact[i];
if((d%2)==1&&(n/d)%2==0)continue;
ans=(ans+mul(f[n/d],mul(quickpow(k,(d+1)/2),(d%2?d:d/2))))%mod;
}
cout<<ans<<"\n";
}
inline void init(){
pfcnt=0;
fcnt=0;
f.clear();
}
int T;
signed main(){
srand(time(0));
linear_sieves();
scanf("%d",&T);
while(T--){
init();
solve();
}
return 0;
}