「ZJOI2017」字符串
LOJ 2572.「ZJOI2017」字符串
\(Description\)
给定一个长度为 \(n\) 的动态字符串,字符集是所有 \(|x| \leq 10^9\) 的整数,要求支持区间加和查询区间最小后缀。
\(Solution\)
一个结论是 一个区间中可能成为最小后缀的后缀数量是 \(O(\log{n})\) 的,证明:
- 考虑两个可能后缀 \(a, b \ (|a| > |b|)\),显然 \(b\) 既是 \(a\) 的前缀也是 \(a\) 的后缀(如果不是的话那么其中有一个一定无法成为最小后缀),那么我们有 \(a\) 存在长度为 \(|a| - |b|\) 的周期。
- 若 \(|b| < |a| < 2|b|\),则 \(b\) 至少包含一个完整的周期,设 \(a = TTc, \ b = Tc\)(\(c\) 为 \(T\) 的前缀),此时 \(b\) 一定不可能成为最小后缀,因为这种情况下最小后缀要不然选 \(TTc\) 要不然选 \(c\),
- 也就是说相邻的两个可能后缀满足 \(|a| \geq 2|b|\)。
- 所以一个区间中可能成为最小后缀的数量是 \(O(\log{n})\) 的。
于是可以用线段树维护 区间中可能成为最小后缀的集合。考虑比较两个后缀,因为是动态的所以 \(sa\) 之类的也许就做不了了,但是我们可以二分+ 字符串 \(hash\) 做到查询 \(O(\log{n})\),分块动态维护字符串 \(hash\) \(O(m \sqrt{n})\)。
这样子一次查询的时间复杂度就是 \(O(\log^3{n})\),那么对于询问我们只需要分为 \(log\) 个区间然后查询即可,\(m\) 比较小 (\(m \leq 3 \times 10^4\)) 所以能过。
总时间复杂度 \(O(n \log^2{n} + m \sqrt{n} + m \log^3{n})\)。
\(Code\)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define N 200000
#define M 500
#define fo(i, x, y) for(ll i = x, end_##i = y; i <= end_##i; ++ i)
#define fd(i, x, y) for(ll i = x, end_##i = y; i >= end_##i; -- i)
#define Max(x, y) (x > y ? x : y)
#define ll long long
void read(ll &x) {
char ch = getchar(); x = 0; ll f = 1;
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + ch - 48, ch = getchar();
x *= f;
}
ll Fast(ll x, ll p, ll Mod) {
ll res = 1;
while (p) {
if (p & 1)
res = 1ll * res * x % Mod;
x = 1ll * x * x % Mod;
p >>= 1;
}
return res;
}
ll lk[N + 1][2], li[N + 1], a[N + 1];
ll n, Q, sqrt_n, suf, tot;
struct HASH {
ll mod, p, inv_p;
ll has[N + 1], inv[N + 1], mi[N + 1], ak[N + 1], ck[N + 1], add[M + 1], bk[M + 1];
void Init(ll Mod, ll P) {
mod = Mod, p = P, inv_p = Fast(p, Mod - 2, Mod);
mi[0] = inv[0] = 1, has[0] = ak[0] = 0;
fo(i, 1, n) mi[i] = 1ll * mi[i - 1] * p % mod;
fo(i, 1, n) inv[i] = 1ll * inv[i - 1] * inv_p % mod;
fo(i, 1, n) has[i] = (has[i - 1] + 1ll * mi[i - 1] * a[i] % mod) % mod;
fo(i, 1, n) ak[i] = (ak[i - 1] + mi[i - 1]) % mod;
fo(i, 1, n) ck[i] = (ak[i] - ak[lk[li[i]][0] - 1] + mod) % mod;
fo(i, 0, tot) bk[i] = add[i] = 0;
}
#define Has(u) (((has[u] + 1ll * ck[u] * ((bk[li[u]] + mod) % mod) % mod) % mod + add[li[u]]) % mod)
ll Hash(ll x, ll y) {
return 1ll * (Has(y) - Has(x - 1) + mod) % mod * inv[x - 1] % mod;
}
ll Hash(ll x) {
return 1ll * (Has(x) - Has(x - 1) + mod) % mod * inv[x - 1] % mod;
}
void Rebuild(ll k, ll u, ll v, ll w) {
fo(i, lk[k][0], lk[k][1]) has[i] = Has(i);
fo(i, lk[k][0], lk[k][1])
a[i] += bk[k];
fo(i, u, v) a[i] += w;
has[lk[k][0]] = (Has(lk[k][0] - 1) + 1ll * mi[lk[k][0] - 1] * a[lk[k][0]] % mod) % mod;
fo(i, lk[k][0] + 1, lk[k][1])
has[i] = (has[i - 1] + 1ll * mi[i - 1] * a[i] % mod) % mod;
add[k] = bk[k] = 0;
}
void Add(ll l, ll r, ll w) {
if (li[l] == li[r]) {
Rebuild(li[l], l, r, w);
fo(i, li[r] + 1, tot)
add[i] = (add[i] + 1ll * (((ak[r] - ak[l - 1] + mod) % mod) * ((w + mod) % mod) % mod)) % mod;
return;
}
Rebuild(li[l], l, lk[li[l]][1], w);
fo(i, li[l] + 1, li[r] - 1) {
bk[i] += w;
add[i] = (add[i] + 1ll * (((ak[lk[i][0] - 1] - ak[l - 1] + mod) % mod) * ((w + mod) % mod) % mod)) % mod;
}
Rebuild(li[r], lk[li[r]][0], r, w);
fo(i, li[r] + 1, tot)
add[i] = (add[i] + 1ll * (((ak[r] - ak[l - 1] + mod) % mod) * ((w + mod) % mod) % mod)) % mod;
}
} h1;
bool Pd(ll x, ll y, ll u, ll v) {
return h1.Hash(x, y) == h1.Hash(u, v);
}
ll LCP(ll u, ll v, ll w) {
ll l = 1, r = w - Max(u, v) + 1, mid = 0;
while (l <= r) {
mid = l + r >> 1;
Pd(u, u + mid - 1, v, v + mid - 1) ?
l = mid + 1
:
r = mid - 1;
}
return l - 1;
}
int Cmp(ll u, ll v, ll w) {
ll k = LCP(u, v, w);
if (k + Max(u, v) > w) return 0;
return h1.Hash(u + k) - h1.Hash(v + k);
}
struct Tree {
struct Suf {
ll x[20], cnt;
} s[N << 2];
ll lazy[N << 2];
void Pushup(ll t, ll r) {
int lson = t << 1, rson = t << 1 | 1;
s[t].cnt = s[rson].cnt;
fo(i, 1, s[t].cnt) s[t].x[i] = s[rson].x[i];
fo(i, 1, s[lson].cnt) {
while (1) {
int x = s[lson].x[i], y = s[t].x[s[t].cnt];
int c = Cmp(x, y, r);
if (c > 0) break;
if (! c) {
if ((r - y + 1 << 1) > r - x + 1) -- s[t].cnt;
s[t].x[++ s[t].cnt] = x;
break;
}
if (! -- s[t].cnt) {
s[t].x[++s[t].cnt] = x;
break;
}
}
}
}
#define lson (t << 1)
#define rson (t << 1 | 1)
void Init(ll t, ll l, ll r) {
if (l == r) {
s[t] = (Suf) { { 0, l }, 1 };
return;
}
int mid = l + r >> 1;
Init(lson, l, mid), Init(rson, mid + 1, r);
Pushup(t, r);
}
void Chang(ll t, ll l, ll r, ll x, ll y) {
if (x <= l && r <= y) return;
int mid = l + r >> 1;
if (x <= mid) Chang(lson, l, mid, x, y);
if (y > mid) Chang(rson, mid + 1, r, x, y);
Pushup(t, r);
}
void Find(ll t, ll l, ll r, ll x, ll y, ll R) {
if (x <= l && r <= y) {
fo(j, 1, s[t].cnt) {
if (Cmp(s[t].x[j], suf, R) < 0)
suf = s[t].x[j];
}
return;
}
int mid = l + r >> 1;
if (y > mid) Find(rson, mid + 1, r, x, y, R);
if (x <= mid) Find(lson, l, mid, x, y, R);
}
#undef lson
#undef rson
} tr;
void Ycl() {
sqrt_n = sqrt(n + 1);
lk[1][0] = li[1] = tot = 1;
fo(i, 2, n) {
li[i] = tot;
if (i - lk[tot][0] + 1 >= sqrt_n)
lk[tot][1] = i, lk[ ++ tot ][0] = i + 1;
}
lk[tot][0] <= n ? lk[tot][1] = n : -- tot;
h1.Init(19260817, 29);
tr.Init(1, 1, n);
}
const ll inf = 2e4;
int main() {
freopen("string.in", "r", stdin);
freopen("string.out", "w", stdout);
read(n), read(Q);
fo(i, 1, n) read(a[i]);
fo(i, 1, n) a[i] += inf;
Ycl();
ll opt, x, y, d;
fo(qu, 1, Q) {
read(opt), read(x), read(y);
if (opt == 1) {
read(d);
h1.Add(x, y, d);
tr.Chang(1, 1, n, x, y);
} else {
suf = y;
tr.Find(1, 1, n, x, y, y);
printf("%lld\n", suf);
}
}
return 0;
}