一直没敢动,但发现前几天做过一道类似的回文串计数,发现这道题也没有想象中的难
考虑一个回文串能转出来几个不同的串
显然是循环节大小这么多个,考虑让一个回文串的贡献为它的循环节的大小
但发现这样子是算重了的,一个回文串有可能转出一个更它不同的回文串
发现这种情况只会在串长为偶数的情况出现一次,于是我们让偶数串的贡献为
1
/
2
1/2
1/2 的循环节大小即可
用一个函数
h
(
n
)
h(n)
h(n) 来表示,当
2
∣
n
2|n
2∣n 时,
h
(
n
)
=
n
2
h(n)=\frac{n}{2}
h(n)=2n,否则为
n
n
n,求的就是
a
n
s
=
∑
d
∣
n
f
(
d
)
h
(
d
)
ans=\sum_{d|n}f(d)h(d)
ans=d∣n∑f(d)h(d)
f
(
d
)
f(d)
f(d) 表示循环节大小为
d
d
d 长度为
n
n
n 的回文串个数
考虑到令
g
(
n
)
g(n)
g(n) 为长度为
n
n
n 的回文串总个数,那么枚举循环节大小就可以不重不漏地统计
g
g
g
g
(
n
)
=
∑
d
∣
n
f
(
d
)
⇔
f
(
n
)
=
∑
d
∣
n
μ
(
n
d
)
g
(
d
)
g(n)=\sum_{d|n}f(d)\Leftrightarrow f(n)=\sum_{d|n}\mu (\frac{n}{d})g(d)
g(n)=d∣n∑f(d)⇔f(n)=d∣n∑μ(dn)g(d)
代入原式
A
n
s
=
∑
d
∣
n
h
(
d
)
∑
l
∣
d
μ
(
d
l
)
g
(
l
)
=
∑
l
∣
n
g
(
l
)
∑
t
l
∣
n
h
(
t
l
)
μ
(
t
)
Ans=\sum_{d|n}h(d)\sum_{l|d}\mu(\frac{d}{l})g(l)=\sum_{l|n}g(l)\sum_{tl|n}h(tl)\mu(t)
Ans=d∣n∑h(d)l∣d∑μ(ld)g(l)=l∣n∑g(l)tl∣n∑h(tl)μ(t)
想办法把
h
h
h 拆开,
h
(
t
l
)
≠
t
∗
h
(
l
)
h(tl)\neq t*h(l)
h(tl)=t∗h(l) 当且仅当
t
t
t 为偶数但
l
l
l 为奇数
注意到如果
μ
(
t
)
≠
0
\mu(t)\neq 0
μ(t)=0 ,那么
t
t
t 中只有一个 2,也就是说
μ
(
t
)
h
(
t
l
)
=
−
μ
(
t
2
)
h
(
t
l
2
)
\mu(t)h(tl)=-\mu(\frac{t}{2})h(\frac{tl}{2})
μ(t)h(tl)=−μ(2t)h(2tl),奇偶抵消
所以,若
l
l
l 为奇数,
n
l
\frac{n}{l}
ln 为偶数,那么
∑
t
∣
n
l
μ
(
t
)
h
(
t
l
)
=
0
\sum_{t|\frac{n}{l}}\mu(t)h(tl)=0
t∣ln∑μ(t)h(tl)=0
特判掉,于是有
A
n
s
=
∑
l
∣
n
g
(
l
)
h
(
l
)
∑
t
l
∣
n
t
μ
(
t
)
Ans=\sum_{l|n}g(l)h(l)\sum_{tl|n}t\mu(t)
Ans=l∣n∑g(l)h(l)tl∣n∑tμ(t)
如何快速求后面的一坨,有贡献的
μ
\mu
μ 一个质因子之多一个
考虑把所有质因子排成一排,重复的往上类,就是每一列可以选一个或者不选,贡献是
p
∗
(
−
1
)
p*(-1)
p∗(−1)
类似构造生成函数,有
∑
t
l
∣
n
t
μ
(
t
)
=
∏
(
1
−
p
i
)
\sum_{tl|n}t\mu(t)=\prod(1-p_i)
tl∣n∑tμ(t)=∏(1−pi)
p
o
l
l
a
r
d
r
h
o
pollard\ rho
pollard rho 分解
d
f
s
dfs
dfs 求出质因子以及
∏
(
1
−
p
i
)
\prod(1-p_i)
∏(1−pi) 即可
#include<bits/stdc++.h>
#define cs const
using namespace std;
typedef long long ll;
#define int long long
int Mod;
ll add(ll a, ll b, ll p){ return a + b >= p ? a + b - p : a + b; }
ll mul(ll a, ll b, ll p){ return (a*b-(ll)((long double)a/p*b)*p+p)%p; }
ll ksm(ll a, ll b, ll p){ ll ans=1; for(;b;b>>=1,a=mul(a,a,p)) if(b&1) ans=mul(ans,a,p); return ans; }
ll gcd(ll a, ll b){ return !b ? a : gcd(b, a % b); }
ll fix(ll a){ return (a % Mod + Mod) % Mod; }
cs int N = 1e7 + 5;
int prim[N], pc, mn[N]; bool isp[N];
void Sieve(int n){
for(int i = 2; i <= n; i++){
if(!isp[i]) prim[++pc] = i, mn[i] = i;
for(int j = 1; j <= pc; j++){
if(prim[j] * i > n) break; isp[prim[j] * i] = true;
mn[i*prim[j]] = mn[i]; if(i % prim[j] == 0) break;
}
}
}
cs int M = 1e6 + 5;
int T; ll k, n; ll p[M]; int tot, ct[M];
int c[20] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43};
bool Miller(ll p){
if(p <= 1e7) return !isp[p];
if(p % 2 == 0) return false;
for(int i = 0; i < 14; i++){
if(p % c[i] == 0) return false;
ll x = p-1, bit = 0;
while(x & 1 ^ 1) x >>= 1, ++bit;
ll cur = ksm(c[i], x, p);
for(int i = 0; i < bit; i++){
ll nxt = mul(cur, cur, p);
if(nxt == 1 && (cur != 1 && cur != p-1)) return false;
cur = nxt; if(cur == 1) break;
} if(cur != 1) return false;
} return true;
}
ll Solve(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 = 1, m = 1, c = rand() % (x-1) + 1;
for(ll k = 2; ; k <<= 1, m = n){
ll q = 1;
for(ll i = 0; i < k; i++){
n = add(mul(n, n, x), c, x);
q = mul(q, abs(m - n), x);
} ll t = gcd(q, x); if(t > 1) return t;
}
}
void Rho(ll x){
if(Miller(x)){ p[++tot] = x; return; }
if(x <= 1e7){
while(x>1){
p[++tot] = mn[x];
int nx = mn[x]; while(x % nx == 0) x /= nx;
} return;
}
ll p = x; while(p >= x) p = Solve(p);
Rho(p); while(x % p == 0) x/=p; Rho(x);
}
ll vl[M]; int num;
unordered_map<ll,ll> F;
void dfs(int u, ll val, ll coef){
if(u > tot){ vl[++num] = val; F[val] = coef; return; }
for(ll i = 0, nx = 1; i <= ct[u]; i++, nx *= p[u])
dfs(u + 1, val * nx, coef * (i ? (Mod+1 - fix(p[u])) : 1) % Mod);
}
void Clear(){ memset(ct,0,sizeof(int)*(tot+1)); num = tot = 0; F.clear(); }
void Solve(){
cin >> n >> k >> Mod; Rho(n); k %= Mod;
sort(p + 1, p + tot + 1);
tot = unique(p + 1, p + tot + 1) - (p + 1); ll nx = n;
for(int i = 1; i <= tot; i++){
while(nx % p[i] == 0) nx /= p[i], ++ct[i];
}
dfs(1, 1, 1); ll ans = 0;
for(int i = 1; i <= num; i++){
ll l = vl[i]; if((l&1)&&((n/l)&1^1)) continue;
ll G = ksm(k, ((l+1)/2), Mod) % Mod, H = (l&1?l:l/2) % Mod;
ans = (ans + G * H % Mod * F[n/l] % Mod) % Mod;
} cout << fix(ans) << '\n';
}
signed main(){
Sieve(1e7); srand(time(0));
cin >> T; while(T--) Solve(), Clear(); return 0;
}