1. 程式人生 > 實用技巧 >「ZJOI2017」字串

「ZJOI2017」字串

「ZJOI2017」字串

\(Description\)

給定一個長度為 \(n\) 的動態字串,字符集是所有 \(|x| \leq 10^9\) 的整數,要求支援區間加和查詢區間最小字尾。

\(Solution\)

首先考慮一個靜態的問題(查詢區間 \((l, r)\) 的最小字尾):將區間分半為 \((l, mid), \ (mid + 1, r)\),那麼整個區間的最小字尾就是左區間的最小字尾在後面加上 \(s[mid + 1..r]\) 與右區間的最小字尾相比較。

受到靜態做法的啟發,思考能否用線段樹維護 區間中可能成為最小字尾的集合,那麼查詢時我們只需要將詢問區間分為 \(log\)

個區間然後合併即可。

一個結論是 一個區間中可能成為最小字尾的字尾數量是 \(O(\log n)\),證明:

  • 考慮兩個可能字尾 \(a, b \ (|a| > |b|)\),顯然 \(b\) 既是 \(a\) 的字首也是 \(a\) 的字尾(如果不是的話那麼其中有一個一定無法成為最小字尾)
  • \(|b| < |a| < 2|b|\),則 \(a, b\) 存在長度為 \(|a| - |b|\) 的週期,設 \(a = ccc, \ b = cc\),那麼 \(b\) 一定不可能成為最小字尾(這種情況下最小字尾要不然選 \(ccc\) 要不然選 \(c\)),
  • 也就是說相鄰的兩個可能字尾滿足 \(|a| \geq |b|\)

於是我們便可以用線段樹維護了,由於 \(m\) 比較小所以查詢 \(O(\log^3 n)\) 就行。

還需要考慮比較兩個字尾,因為是動態的所以 \(sa\) 之類的也許就做不了了,但是我們可以可以用二分+ \(hash\) 做到 \(O(logn)\),可以分塊動態維護 \(hash\)

總時間複雜度 \(O(n \log^2 n + n \sqrt{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 lson (t << 1)
#define rson (t << 1 | 1)
#define Max(x, y) (x > y ? x : y)
#define Min(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;
    }

    ll Has(ll u) {
        return ((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) {
    if (u > v) swap(u, v);
    ll l = 1, r = w - v + 1, mid = 0, k = 0;
    while (l <= r) {
        mid = l + r >> 1;
        if (Pd(u, u + mid - 1, v, v + mid - 1))
            l = (k = mid) + 1;
        else
            r = mid - 1;
    }
    return k;
}

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);
}

ll test1 = 0, test2 = 0;

int test = 0;

struct Tree {
    struct Suf {
        ll x[40], cnt;
    } s[N << 2];
    ll lazy[N << 2];
    void Pushup(ll t, ll l, ll r) {
        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 (2 * (r - y + 1) > r - x + 1) s[t].cnt--;
                    s[t].x[++ s[t].cnt] = x;
                    break;
                }
                s[t].cnt--;
                if (!s[t].cnt) {
                    s[t].x[++s[t].cnt] = x;
                    break;
                }
            }
        }
    }
    void Init(ll t, ll l, ll r) {
        if (l == r) {
            s[t] = (Suf) {
                { 0, l }, 1
            };
            return;
        }
        ll mid = l + r >> 1;
        Init(lson, l, mid), Init(rson, mid + 1, r);
        Pushup(t, l, r);
    }
    void Chang(ll t, ll l, ll r, ll x, ll y) {
        if (x <= l && r <= y) return;
        ll 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, l, r);
    }
    void Find(ll t, ll l, ll r, ll x, ll y, ll R) {
        if (test == 49 && t == 9)
            Pushup(t, l, 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;
        }
        ll 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);
    }
} 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, 233333333);
    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;

    test = 0;

    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 {
            ++ test;
            suf = y;
            if (x < y)
                tr.Find(1, 1, n, x, y, y);
            printf("%lld\n", suf);
        }
    }

    return 0;
}