Address
Solution
前置知识:广义欧拉定理。
当
时:
而题目中的修改:
实际上是:
由于
不断地变成
之多
次后会变成
,
所以一个位置最多被修改
之后就不会变。
所以,我们用线段树维护区间和以及区间内所有位置被修改的最少次数,
修改时遍历到叶子节点,如果遍历到一个节点,这个节点被修改的次数达到了上限,就 return 掉。
这样,每个叶子节点到根的路径最多被修改
次。
我们需要对于每个
和
,预处理出:
(共
个
)
根据广义欧拉定理,上式等于:
递归求解。
注意两个细节:
(1)
的幂。注意到指数最多只有
,所以可以预处理出
,
,一直到
的值,然后再处理
,
,
, …… 的值,则:
(2)注意当指数小于
则降幂时不能加上
。
复杂度
。
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;
}