[做题记录] Luogu P4607 [SDOI2018]反回文串 题解

题意

「回文串什么的最讨厌了……」

小 Q 讨厌任何形式的回文串:

  1. 如果一个字符串从左往右读和从右往左读是一样的,那么小 Q 讨厌它;例如 aaaba
  2. 对于一个字符串来说,若将某个前缀子串移除并拼接到字符串的尾部,能得到一个小 Q 讨厌的字符串,那么小 Q 也会讨厌原来的这个字符串;例如 aabbaa

现在问题来了,如果任意字符串只可以由 \(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\) 的字符串的个数。

那么答案就是 :

\[\sum_{d|n}f(d) h(d) \]

又因为

\[\sum_{d|n}f(d)=k^{\lceil\frac n2\rceil}=g(n) \]

所以

\[f(n) = \sum_{d|n}g(d) \mu(\frac{n}{d}) \\ ans = \sum_{i | n}g(i)\sum_{d | \frac{n}{i}}\mu(d)h(id) \]

然后这里思路就又卡住了。

考虑把 \(h(id)\) 拆开来看, 变成 \(d \times h(i)\)

观察 \(h(id) \neq d \times h(i)\) 的时候当且仅当 \(d\) 为偶数, 且 \(i\) 为奇数, 注意此时 \(\frac{n}{i}\) 也是偶数。

不妨考虑 \(i\) 为奇数, \(\frac{n}{i}\) 是偶数这个条件下, 下面式子的取值:

\[\sum_{d|\frac{n}{i}}\mu(d)h(id) \]

由于 \(\frac{n}{i}\) 是偶数, 所以可以对于 \(d\) 是否是偶数分别讨论, 发现此时 \(\mu\) 相反, \(h\) 相等, 恰好是 \(0\)

那么可以变换原来的式子 :

\[ans = \sum_{i|n}g(i)f(i)\sum_{d|\frac{n}{i}}d\mu(d) \]

注意虽然前面说明了那个情况下没有贡献, 但是枚举的时候要注意 \(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;
}
posted @ 2021-12-20 22:08  HN-wrp  阅读(57)  评论(0编辑  收藏  举报