1. 程式人生 > >[BZOJ4869][Shoi2017]相逢是問候(廣義尤拉定理+線段樹)

[BZOJ4869][Shoi2017]相逢是問候(廣義尤拉定理+線段樹)

Address

洛谷P3747
BZOJ4869
LOJ#2145

Solution

前置知識:廣義尤拉定理。
b ϕ ( p ) b\ge\phi(p)

時:
a b a b   m
o d   ϕ ( p ) +
ϕ ( p ) (   m o d   p ) a^b\equiv a^{b\bmod\phi(p)+\phi(p)}(\bmod p)

而題目中的修改:
c c c . . . a i c^{c^{c^{...^{a_i}}}}
實際上是:
c c c . . . a i   m o d   ϕ ( p ) + ϕ ( p ) = c c c . . . a i   m o d   ϕ ( ϕ ( p ) ) + ϕ ( ϕ ( p ) )   m o d   ϕ ( p ) + ϕ ( p ) c^{c^{c^{...^{a_i}}}\bmod\phi(p)+\phi(p)}=c^{c^{c^{...^{a_i}}\bmod\phi(\phi(p))+\phi(\phi(p))}\bmod\phi(p)+\phi(p)}
由於 p p 不斷地變成 ϕ ( p ) \phi(p) 之多 O ( log p ) O(\log p) 次後會變成 1 1
所以一個位置最多被修改 O ( log p ) O(\log p) 之後就不會變。
所以,我們用線段樹維護區間和以及區間內所有位置被修改的最少次數,
修改時遍歷到葉子節點,如果遍歷到一個節點,這個節點被修改的次數達到了上限,就 return 掉。
這樣,每個葉子節點到根的路徑最多被修改 O ( log p ) O(\log p) 次。
我們需要對於每個 a i a_i j j ,預處理出:
F ( p , a i , j ) = c c c . . . a i   m o d   p F(p,a_i,j)=c^{c^{c^{...^{a_i}}}}\bmod p
(共 j j c c
根據廣義尤拉定理,上式等於:
c F ( ϕ ( p ) , a i , j 1 ) + ϕ ( p ) c^{F(\phi(p),a_i,j-1)+\phi(p)}
遞迴求解。
注意兩個細節:
(1) c c 的冪。注意到指數最多隻有 2 × 1 0 8 2\times10^8 ,所以可以預處理出 c 0 c^0 c 1 c^1 ,一直到 c 2 × 1 0 4 1 c^{2\times 10^4-1} 的值,然後再處理 c 2 × 1 0 4 c^{2\times 10^4} ( c 2 × 1 0 4 ) 2 (c^{2\times 10^4})^2 ( c 2 × 1 0 4 ) 3 (c^{2\times 10^4})^3 , …… 的值,則:
c x = ( c 2 × 1 0 4 ) x 2 × 1 0 4 × c x   m o d   ( 2 × 1 0 4 ) c^x=(c^{2\times 10^4})^{\lfloor\frac{x}{2\times10^4}\rfloor}\times c^{x\bmod (2\times10^4)}
(2)注意當指數小於 ϕ ( p ) \phi(p) 則降冪時不能加上 ϕ ( p ) \phi(p)
複雜度 O ( n ( log n log p + log 2 p ) ) O(n(\log n\log p+\log^2p))

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define p2 p << 1
#define p3 p << 1 | 1
using namespace std;

inline int read()
{
	int res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res