「ZJOI2017」字符串(old)

传送门

 

Description


给定一个长为 n 的字符串.

要求支持区间加, 以及查询某段区间中最小后缀的起点位置(终点为区间右端点).

 

Solution


考虑查询区间 \((l, r)\), 可以将其分为区间 \((l, p)\)\((p + 1, r)\),

那么区间 \((l, r)\) 的最小后缀就是区间 \((l, p)\) 所有最小可能后缀在后面怼上一个字符串 \(s[p + 1 ... r]\) 与 区间 \((p + 1, r)\) 中字典序最小的后缀再取字典序最小

一个前置的小东西 :

对于一个区间中可能成为最小后缀的字符串 \(s_1\), \(s_2\) (\(|s_1|\) > \(|s_2|\)), \(s_2\)\(s_1\) 的前缀

然后可以发现一个性质 :

对于区间中的两个可能成为最小后缀的字符串 \(s_1\), \(s_2\), 若 \(|s_2| < |s_1| < 2|s_2|\)\(s_2\) 不为区间中字典序最小的后缀, 那么 \(s_2\) 是不可能成为最小后缀的.

证明也很简单 :

由于 \(|s_2| < |s_1| < 2|s_2|\)\(s_2\)\(s_1\) 的前缀, 那么 \(s_1\) 将会有一个长为 \(|s_1| - |s_2|\) 的循环节,
\(s_1 = TTc\), \(s_2 = Tc\), c 是区间中字典序最小的后缀且 c 为 T 的一段前缀, (仔细思考发现可以这么设)
显然要不取 TTc 要不取 c, 所以 \(s_2\) 不可能成为最小后缀

由此, 一个区间中的两个相邻最小可能后缀的长度至少是两倍, 也就是说, 区间中最小可能后缀的数量是\(O(log n)\)的.

所以, 可以用线段树维护最小可能后缀, 合并的时间复杂度就是 \(O(log n)\)的.

由发现的性质可得, 对于线段树上一个端点, 其最多只会取左端点的一个最小可能后缀(正常人的线段树左区间长度都是小于等于右区间的吧)

考虑合并区间 \((l, r)\) :

首先当前节点的最小可能后缀只可能由左儿子中一个最小可能后缀和右儿子中一部分(或全部)最小可能后缀组成
找出左区间所有最小可能后缀在右边怼上右区间字符串的最小后缀 \(s_1\)
对于右区间最长的最小可能后缀 \(s_2\), 分三种情况讨论 :

  1. \(s_2\)\(s_1\) 的前缀, 那么将 \(s_1\) 和 右区间所有最小可能后缀合并
  2. \(s_1\) < \(s_2\), 求出 lcp, 将 \(s_1\) 和 右区间长度小于等于 lcp 的最小可能后缀合并
  3. \(s_1\) > \(s_2\), 舍弃 \(s_1\), 取右区间所有最小可能后缀

比较字典序(其实从实现来看实质上就是求lcp)是 \(O(log n)\) 的,

这样子维护看上去是 \(O(n log^3 n)\) 的, 但实际上远远跑不满, 至少实测能过

(由于不太会计算复杂度所以无法严谨得出酱紫做的复杂度, 但也许这么打是错的唔)

维护好线段树后, 查询只需要分成 \(log\) 个区间, \(O(m log^3 n)\) 做就好
(m 比较小所以是能过的, 但是我常数貌似被卡了呜, 打得太丑?)

最后需要考虑的一件事是 : 怎么求 lcp ?

由于待修, 所以 SA 之类的貌似就不太行了.

所以考虑 hash + 分块维护, 这样就能修改 \(O(\sqrt{n})\) 查询 \(O(1)\).

二分查找lcp即可.

 

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

bool Cmp(ll u, ll v, ll w) {
    ll k = LCP(u, v, w);
   return k <= w - Max(u, v) && h1.Hash(u + k) < h1.Hash(v + k);
}

ll test1 = 0, test2 = 0;

struct Tree {
    struct Suf { ll x[20], cnt; } s[N << 2];
    ll lazy[N << 2];

    void Pushup(ll t, ll l, ll r) {
        int su = s[lson].x[1], mid = l + r >> 1;
        fo(i, 2, s[lson].cnt)
           if (Cmp(mid, su + mid - s[lson].x[i], r))
                su = s[lson].x[i];
        int lcp = LCP(su, s[rson].x[1], r);
        if (lcp == r - s[rson].x[1] + 1) {
            s[t].x[ s[t].cnt = 1 ] = su;
            fo(i, 1, s[rson].cnt)
                s[t].x[ ++ s[t].cnt ] = s[rson].x[i];
        } 
        else
        if (h1.Hash(su + lcp) < h1.Hash(s[rson].x[1] + lcp)) {
            s[t].x[ s[t].cnt = 1 ] = su;
            fd(i, s[rson].cnt, 1) {
                if (r - s[rson].x[i] + 1 > lcp) {
                    fo(j, i + 1, s[rson].cnt)
                        s[t].x[ ++ s[t].cnt ] = s[rson].x[j];
                    break;
                }
            }
        } else {
            s[t] = s[rson];
        }
    }

    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 (l == x && r == y) return;
        ll mid = l + r >> 1;
        if (y <= mid) Chang(lson, l, mid, x, y);
        else if (x > mid) Chang(rson, mid + 1, r, x, y);
        else Chang(lson, l, mid, x, mid), Chang(rson, mid + 1, r, mid + 1, y);
        Pushup(t, l, 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(suf, s[t].x[j], R))
                    suf = s[t].x[j];
            return;
        }
        ll mid = l + r >> 1;
        if (x <= mid) Find(lson, l, mid, x, y, R);
        if (y > mid) Find(rson, mid + 1, r, 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 = 10000;

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

    return 0;
}

posted @ 2020-10-21 21:31  buzzhou  阅读(113)  评论(0)    收藏  举报