[做题记录] Luogu P4607 [SDOI2018]反回文串 题解
题意
「回文串什么的最讨厌了……」
小 Q 讨厌任何形式的回文串:
- 如果一个字符串从左往右读和从右往左读是一样的,那么小 Q 讨厌它;例如
aa
和aba
。- 对于一个字符串来说,若将某个前缀子串移除并拼接到字符串的尾部,能得到一个小 Q 讨厌的字符串,那么小 Q 也会讨厌原来的这个字符串;例如
aab
和baa
。现在问题来了,如果任意字符串只可以由 \(k\) 种已知的字符组成 (也就是说字符集的大小为 \(k\) ),那么长度为 \(n\) 的所有字符串里,有多少字符串是小 Q 讨厌的?
答案可能很大,你只需要给出答案对 \(p\) 取模后的值。
\(T \leq 10, n, k\leq 10^{18}\) , \(p\) 不一定是素数。
题解
这是人做的????
考虑一个计算答案的方式是枚举回文串, 然后这个回文串的所有循环移位进行一次贡献。
但是这样显然会算重, 因为可能出现回文串之间的循环同构, 也有可能串在循环移位的时候会得到自身。
考虑对于每一个回文串, 求一个最小的 \(l\) 使得这个串在循环移位 \(l\) 次以后会再次变成一个回文串, 那么 \(\sum l\) 就是答案。
考虑 \(l\) 的取值在不同字符串下的情况。
一个经典的想法是, 对于最小循环节 \(T\) , 如果 \(T\) 是偶数, 那么 \(l = \frac{T}{2}\) , 否则 \(l = T\)。
那么现在我们考虑对于最小循环节为某个数的字符串计数。
考虑设 \(h(i) = 长度为i的字符串的l = i \times \frac{1 + [i是奇数]}{2}\) , \(f(i)\) 表示最小循环节为 \(i\) 的字符串的个数。
那么答案就是 :
又因为
所以
然后这里思路就又卡住了。
考虑把 \(h(id)\) 拆开来看, 变成 \(d \times h(i)\) 。
观察 \(h(id) \neq d \times h(i)\) 的时候当且仅当 \(d\) 为偶数, 且 \(i\) 为奇数, 注意此时 \(\frac{n}{i}\) 也是偶数。
不妨考虑 \(i\) 为奇数, \(\frac{n}{i}\) 是偶数这个条件下, 下面式子的取值:
由于 \(\frac{n}{i}\) 是偶数, 所以可以对于 \(d\) 是否是偶数分别讨论, 发现此时 \(\mu\) 相反, \(h\) 相等, 恰好是 \(0\) 。
那么可以变换原来的式子 :
注意虽然前面说明了那个情况下没有贡献, 但是枚举的时候要注意 \(i\) 是奇数, \(\frac{n}{i}\) 是偶数的时候, 要跳过去。
然后考虑后面那个东西, 其实就是把 \(\frac{n}{i}\) 质因数分解以后, 求 \(\prod_{i = 1}^k (1 - p_i)\) , 上 \(PR\) 即可。
#include <bits/stdc++.h>
using namespace std;
#define lep(i, l, r) for(int i = (l); i <= (r); i ++)
#define rep(i, l, r) for(int i = (l); i >= (r); i --)
#define Lep(i, l, r) for(int i = (l); i < (r); i ++)
#define Rep(i, l, r) for(int i = (l - 1); i >= (r); i --)
#define debug(...) fprintf (stderr, __VA_ARGS__)
#define pb push_back
#define fi first
#define se second
#define gc getchar
#define pc putchar
using i64 = long long;
using uint = unsigned int;
using ui64 = unsigned long long;
using pii = std :: pair<int, int>;
using vi = std :: vector<int>;
#define int long long
template<typename A, typename B>
inline void Min(A &x, B y) { x = x < y ? x : y; }
template<typename A, typename B>
inline void Max(A &x, B y) { x = x > y ? x : y; }
template<typename T> inline void read(T &x) {
x = 0; char a = gc(); bool f = 0;
for (; ! isdigit(a); a = gc()) if (a == '-') f = 1;
for (; isdigit(a); a = gc()) x = x * 10 + a - '0';
if (f) x = -x;
}
int P = 1e9 + 7;
inline int mod(int x) { return x + (x >> 31 & P); }
inline void sub(int &x, int y) { x = mod(x - y); }
inline void pls(int &x, int y) { x = mod(x + y - P); }
inline int add(int x, int y) { return mod(x + y - P); }
inline int dec(int x, int y) { return mod(x - y); }
inline int power(int x, i64 k) {
int res = 1; if (k < 0) k += P - 1;
while (k) { if (k & 1) res = 1ll * res * x % P; x = 1ll * x * x % P; k >>= 1; }
return res;
}
namespace Pollard_Rho_Space {
mt19937 Rand;
inline i64 mul(i64 x, i64 y, i64 P) {
//i64 res = x * y - (i64) ( (long double) x / P * y + 0.5 ) * P;
//return res < 0 ? res + P : res;
return (__int128) x * y % P;
}
inline i64 power(i64 x, i64 k, i64 P) {
i64 res = 1;
while(k) {
if(k & 1) res = mul(res, x, P);
x = mul(x, x, P); k >>= 1;
} return res;
}
inline bool Miller_Rabbin(i64 n) {
static int pri[10] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 61};
lep (i, 0, 9) if(n % pri[i] == 0) return n == pri[i];
lep (i, 0, 9) {
i64 r = n - 1, b = 0;
while(! (r & 1)) r >>= 1, b ++;
i64 w = power(pri[i], r, n), p = w;
lep (j, 1, b) {
w = mul(w, w, n);
if(w == 1 && p != n - 1 && p != 1) return 0;
p = w;
}
if(w != 1) return 0;
}
return 1;
}
inline i64 run(i64 x, i64 n, i64 c) {
return (mul(x, x, n) + c) % n;
}
inline i64 Pollard(i64 n) {
if(n == 4) return 2;
i64 c = Rand() % (n - 1) + 1, a = 0, b = 0;
a = run(a, n, c);
b = run(b, n, c); b = run(b, n, c);
while(a ^ b) {
i64 g = __gcd(abs(a - b), n);
if(g != 1) return g;
a = run(a, n, c);
b = run(b, n, c); b = run(b, n, c);
}
return n;
}
inline i64 Pollard_Rho(i64 n) {
if(Miller_Rabbin(n)) return -1;
i64 res;
while((res = Pollard(n)) == n);
return res;
}
}
using Pollard_Rho_Space :: Pollard_Rho;
using Pollard_Rho_Space :: Miller_Rabbin;
const int N = 5e5 + 10;
i64 stk[N];
int top;
i64 n, k;
void getfactor(i64 n) {
if(n == 1) return ;
if(Miller_Rabbin(n)) { stk[++ top] = n; return ; }
i64 p = Pollard_Rho(n);
getfactor(n / p); getfactor(p);
}
i64 p[N], q[N];
int cnt;
i64 ans;
int g(i64 n) { return power(k, (n + 1) / 2); }
int h(i64 n) { return (n & 1 ? n : n >> 1) % P; }
void dfs(int i, i64 d, i64 pr) {
//cerr << i << ' ' << d << ' ' << pr << endl;
if(i == cnt + 1) {
if((n / d & 1) && (d & 1) == 0) return ;
ans = (ans + 1ll * g(n / d) * h(n / d) % P * pr % P) % P;
return ;
}
dfs(i + 1, d, pr);
pr = 1ll * pr * (P - p[i] % P + 1) % P;
for(int j = 1; j <= q[i]; j ++) d *= p[i], dfs(i + 1, d, pr);
}
void solve() {
read(n); read(k); read(P);
k %= P;
top = 0;
getfactor(n);
sort(stk + 1, stk + 1 + top);
cnt = 0;
for(int i = 1; i <= top; i ++) {
if(stk[i] != stk[i - 1]) p[++ cnt] = stk[i], q[cnt] = 0;
q[cnt] ++;
}
ans = 0; dfs(1, 1, 1); cout <<ans<<endl;
}
signed main() {
int Case; read(Case);
while(Case --) solve();
return 0;
}