[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 = (res << 3) + (res << 1) + (c - 48);
	return bo ? ~res + 1 : res;
}

template <class T>
void Swap(T &a, T &b) {a ^= b; b ^= a; a ^= b;}

template <class T>
T Min(T a, T b) {return a < b ? a : b;}

const int N = 5e4 + 5, M = N << 2, E = 104, ORZ = 2e4, L = 2e4 + 5;
int n, m, ZZQ, c, a[N], tot, PYZ[N], p[N], q[N], sum[M], cnt[M],
f[N][E], g[E][L], h[E][L];
bool ig[E][L], ih[E][L], Flag;

int phi(int n)
{
	int i, j, S = sqrt(n), tot = 0, res = 1;
	For (i, 2, S)
	{
		if (n % i) continue;
		p[++tot] = i; q[tot] = 0;
		while (n % i == 0) q[tot]++, n /= i;
	}
	if (n > 1) p[++tot] = n, q[tot] = 1;
	For (i, 1, tot)
	{
		res *= p[i] - 1;
		For (j, 1, q[i] - 1) res *= p[i];
	}
	return res;
}

int powx(int x, int p)
{
	return 1ll * h[x][p / ORZ] * g[x][p % ORZ] % PYZ[x];
}

bool chk(int x, int p)
{
	return ih[x][p / ORZ] || ig[x][p % ORZ]
		|| 1ll * h[x][p / ORZ] * g[x][p % ORZ] >= PYZ[x];
}

int F(int x, int a, int p)
{
	if (!x) return Flag = p || a, 0;
	if (!p) return Flag = a >= PYZ[x], a % PYZ[x];
	int tmp = F(x - 1, a, p - 1), res;
	if (Flag) res = powx(x, tmp + PYZ[x - 1]);
	else res = powx(x, tmp);
	return Flag = c == 1 ? PYZ[x] == 1 :
		Flag || chk(x, tmp), res;
}

void build(int l, int r, int p)
{
	if (l == r) return (void) (sum[p] = a[l]);
	int mid = l + r >> 1;
	build(l, mid, p2); build(mid + 1, r, p3);
	sum[p] = (sum[p2] + sum[p3]) % ZZQ;
}

void upt(int p)
{
	sum[p] = (sum[p2] + sum[p3]) % ZZQ;
	cnt[p] = Min(cnt[p2], cnt[p3]);
}

void change(int l, int r, int s, int e, int p)
{
	if (cnt[p] >= tot + 2) return;
	if (l == r) return (void) (sum[p] = f[l][++cnt[p]]);
	int mid = l + r >> 1;
	if (e <= mid) change(l, mid, s, e, p2);
	else if (s >= mid + 1) change(mid + 1, r, s, e, p3);
	else change(l, mid, s, mid, p2),
		change(mid + 1, r, mid + 1, e, p3);
	upt(p);
}

int ask(int l, int r, int s, int e, int p)
{
	if (l == s && r == e) return sum[p];
	int mid = l + r >> 1;
	if (e <= mid) return ask(l, mid, s, e, p2);
	else if (s >= mid + 1) return ask(mid + 1, r, s, e, p3);
	else return (ask(l, mid, s, mid, p2)
		+ ask(mid + 1, r, mid + 1, e, p3)) % ZZQ;
}

int main()
{
	int i, j, p, op, l, r;
	n = read(); m = read(); p = ZZQ = read(); c = read();
	For (i, 1, n) a[i] = read();
	PYZ[tot = 0] = ZZQ;
	while (p > 1) p = PYZ[++tot] = phi(p);
	For (i, 0, tot >> 1)
		if (i != tot - i) Swap(PYZ[i], PYZ[tot - i]);
	For (i, 0, tot)
	{
		g[i][0] = h[i][0] = 1 % PYZ[i];
		ig[i][0] = ih[i][0] = PYZ[i] == 1;
		For (j, 1, ORZ)
			g[i][j] = 1ll * g[i][j - 1] * c % PYZ[i],
			ig[i][j] = ig[i][j - 1] || 1ll * g[i][j - 1] * c >= PYZ[i];
		For (j, 1, ORZ)
			h[i][j] = 1ll * h[i][j - 1] * g[i][ORZ] % PYZ[i],
			ih[i][j] = ih[i][j - 1] || ig[i][ORZ] ||
				1ll * h[i][j - 1] * g[i][ORZ] >= PYZ[i];
	}
	For (i, 1, n) For (j, 0, tot + 2)
		f[i][j] = F(tot, a[i], j);
	build(1, n, 1);
	while (m--)
	{
		op = read(); l = read(); r = read();
		if (op == 0) change(1, n, l, r, 1);
		else printf("%d\n", ask(1, n, l, r, 1));
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/83009044