题解 P4607 【[SDOI2018]反回文串】
这道题很有意思,需要你进行大量的手玩来推结论
- 结论
1 :一个字符串 A 的不同的轮换的数量为|\sigma(A)| ,其中\sigma(S) 表示 S 的最小循环节。
这个结论比较基础/fad
- 结论
2 :一个回文串的最小循环节\sigma(S) 必然也是一个回文串。
证明的话可以手玩。
- 结论
3 :一个长度为n 的字符串可以被一个最小循环节唯一表示,同时这个循环节也会唯一表示一个长度为n 的字符串。
这个结论也非常显然。
不过我们可以开始转变思路了,我们尝试对
所以我们猜想答案是:
很快你就会意识到不对,我们发现比如字符串 abbaabba 他在一定的循环之后会变成 baabbaab 我们统计 abba 与 baab 都一起算一遍。
我们接着又可以发现这几个结论:
- 结论
4 :如果一个合法的循环节\sigma 的长度为偶数,那么至少存在另一个\sigma 与之对应。
证明的话手玩,大概就是把
- 结论
5 :在结论4 的基础上,每个长度为偶数的合法最小循环节\sigma 都有且仅有一个\zeta 与之对应,即4 的证明下面补充的例子。
这个结论就需要手玩了,画一个图,然后假设一个位移
- 结论
6 :类比结论5 ,如果一个回文最小循环节\sigma 为奇数,那么其不存在任意一个与之对应\zeta 使之回文。
这个结论的手玩方式和
- 结论
7 :约数个数函数的增长十分缓慢,对于n\le 10^{18} ,有\max\{d(n)\}=103680
我们发现结论
而结论
结论
现在我们可以开始计数了,令
其中
可以直接当作用了一次结论
我们知道
于是有:
于是我们知道:
令
到目前为止,如果我们能够做到
我们肯定希望有一种玄幻的操作可以将
假设将
由于
通过打表可以发现这个式子的值恒定为
我们理性分析一下,这个式子的前提为
由于我们不关注
如果能够
的取值,我们仍然只考虑
考虑一个 dp 的思想来统计答案,每次新增一个质因数
我们先通过 Pollard−Rho 将
值得注意的是被统计的
复杂度
#include<bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
#define int __int128
int gi() {
char cc = getchar() ; int cn = 0, flus = 1 ;
while( cc < '0' || cc > '9' ) { if( cc == '-' ) flus = - flus ; cc = getchar() ; }
while( cc >= '0' && cc <= '9' ) cn = cn * 10 + cc - '0', cc = getchar() ;
return cn * flus ;
}
const int N = 10000 + 5 ;
int T, n, m, P, st[N], top, w[N], num, c[N] ;
int mul( int a, int b, int p ) {
return ( a % p ) * ( b % p ) % p ;
}
int fpow( int x, int k, int p ) {
int ans = 1, base = x % p ;
while( k ) {
if( k & 1 ) ans = mul( ans, base, p ) ;
base = mul( base, base, p ), k >>= 1 ;
} return ans % p ;
}
bool M_R( int p ) {
if( p == 2 || p == 3 ) return 1 ;
if( p == 1 || ( p % 2 == 0 ) ) return 0 ;
re int d = p - 1, s = 0 ; while( !( d & 1 ) ) ++ s, d >>= 1 ;
rep( i, 0, 5 ) {
re int a = rand() % ( p - 3 ) + 2 ;
re int x = fpow( a, d, p ), y = 0 ;
for( register int j = 0; j < s; ++ j ) {
y = mul( x, x, p ) ; if( y == 1 && ( x != 1 ) && x != ( p - 1 ) ) return 0 ;
x = y ;
} if( y != 1 ) return 0 ;
} return 1 ;
}
int gcd( int x, int y ) {
return ( x == 0 ) ? y : gcd( y % x, x ) ;
}
int Rand( int x ) {
return 1ll * ((rand() << 15 ^ rand()) << 30 ^ (rand() << 15 ^ rand())) % x ; //2
}
int work( int p ) {
re int k = 2, x = Rand(p - 1) + 1, y = x, d = 1, c = Rand(p) % 999997 ;
for( re int i = 1; d == 1; ++ i ) {
x = ( mul( x, x, p ) + c ) % p ;
d = gcd( (x > y) ? x - y : y - x, p ) ;
if( i == k ) y = x, k <<= 1 ;
} return d ;
}
void Pollard_Rho(int p) {
if( p == 1 ) return ;
if( M_R(p) ) { st[++ top] = p; return ; }
int x = p ; while( x == p ) x = work(x) ;
Pollard_Rho(x), Pollard_Rho(p / x) ;
}
int Ans = 0 ;
void dfs( int x, int f, int d, int p ) {
if( x == num + 1 ) {
if( ( (d & 1) ) && ( !( (n / d) & 1 ) ) ) return ;
int g = ( d & 1 ) ? d : d / 2 ;
Ans = ( Ans + mul( mul( fpow( m, ( d + 1 ) / 2, p ), g, p ), (f + p) % p, p ) + p ) % p ;
return ;
}
int fd = 1 ;
rep( i, 0, c[x] ) {
if( i == c[x] ) dfs( x + 1, f, d * fd, p ) ;
else dfs( x + 1, f * ( 1 - w[x] ), d * fd, p ) ;
fd = fd * w[x] ;
}
}
signed main()
{
srand(time(NULL)) ;
T = gi() ;
while( T-- ) {
n = gi(), m = gi(), P = gi(), top = 0, num = 0, Ans = 0 ;
memset( c, 0, sizeof(c) ), memset( w, 0, sizeof(w) ), memset( st, 0, sizeof(st) ) ;
Pollard_Rho(n) ;
sort( st + 1, st + top + 1 ) ;
rep( i, 1, top ) {
if( st[i] == st[i - 1] ) ++ c[num] ;
else w[++ num] = st[i], c[num] = 1 ;
}
dfs( 1, 1, 1, P ) ;
printf("%lld\n", (long long)((Ans) % P) ) ;
}
return 0 ;
}