Loading

Luogu12490 [集训队互测 2024] 字符串 做题记录

link

有了 NOI2023 那个题为背景,我们容易想到先后缀排序,对于询问 \((i, r)\) 先求出 \(rk_i < rk_{i + l}\) 的数量。

此时需要减去 \(s[i, i + l - 1] = s[i + l, i + 2l - 1]\) 并且 \(rk_i < rk_{i + l}\) 的数量。

这需要一个 trick:对于所有平方串,可以用 \(\mathcal O(n\log n)\) 个三元组 \((l, r, k)\) 表示,其中平方串长度为 \(2k\),开头为 \(l\sim r\)

具体来说,我们先枚举 \(k\) 表示长度,然后用一个大家比较知悉的 trick:定长分块求 lcs 和 lcp。

所以我们可以枚举 \(i = k, 2k, \dots\),然后考虑加入以 \(i - k + 1 \sim i\) 开头的长度为 \(2k\) 的平方串。

可以求出 \(\text{lcs}(i, i + k) = u, \ \text{lcp}(i, i + k) = v\),那么有 \(l = \max(i - k + 1, i), \ r = \min(i, i + v - k)\)

如果 \(l \le r\) 则可以加入 \((l, r, k)\)

现在回到原问题,不难发现,对于任意 \((l, r, k)\)\(l\le x < y\le r\)\(rk_x, rk_{x + k}\)\(rk_y, rk_{y + k}\) 的大小关系相同。所以我们可以通过判断是否有 \(rk_l < rk_{l + k}\) 来决定是否加入 \((l, r, k)\)

然后对于每个询问 \((i_0, r_0)\),对于每个三元组 \((l, r, k)\),如果 \(l\le i_0\le r\) 并且 \(k\le r_0\) 则有贡献。

同样是二维数点,时间复杂度 \(\mathcal O(n\log ^ 2n)\)


这题记录的原因主要还是积累一下 trick。

在如何处理平方串上我想到了用 runs,但是事实上是不好维护的,因为位置序列为 \(x, x + 2p, x + 4p, \dots\) 并不连续。


点击查看代码
#include <bits/stdc++.h>
#define ll int
#define LL long long
#define uLL unsigned LL
#define fi first
#define se second
#define mkp make_pair
#define pir pair<ll, ll>
#define pb push_back
#define i128 __int128
using namespace std;
char buf[1 << 22], *p1, *p2;
// #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, (1 << 22) - 10, stdin), p1 == p2)? EOF :
// *p1++)
template <class T>
const inline void rd(T &x) {
    char ch;
    bool neg = 0;
    while (!isdigit(ch = getchar()))
        if (ch == '-')
            neg = 1;
    x = ch - '0';
    while (isdigit(ch = getchar())) x = (x << 1) + (x << 3) + ch - '0';
    if (neg)
        x = -x;
}
const ll maxn = 5e5 + 10, mod = 998244353; const LL inf = 1e18;
ll power(ll a, ll b = mod - 2, ll p = mod) {
    ll s = 1;
    while (b) {
        if (b & 1)
            s = 1ll * s * a % p;
        a = 1ll * a * a % p, b >>= 1;
    }
    return s;
}
template <class T, class _T>
const inline ll pls(const T x, const _T y) { return x + y >= mod ? x + y - mod : x + y; }
template <class T, class _T>
const inline ll mus(const T x, const _T y) { return x < y ? x + mod - y : x - y; }
template <class T, class _T>
const inline void add(T &x, const _T y) { x = x + y >= mod ? x + y - mod : x + y; }
template <class T, class _T>
const inline void sub(T &x, const _T y) { x = x < y ? x + mod - y : x - y; }
template <class T, class _T>
const inline void chkmax(T &x, const _T y) { x = x < y ? y : x; }
template <class T, class _T>
const inline void chkmin(T &x, const _T y) { x = x < y ? x : y; }

ll n, q, rk[maxn], sa[maxn], cnt[maxn], oldrk[maxn], id[maxn];
char str[maxn];

void SA() {
    for(ll i = 1; i <= n; i++) ++cnt[str[i] - 'a'];
    for(ll i = 1; i < 26; i++) cnt[i] += cnt[i - 1];
    ll p = 0;
    for(ll i = n; i; i--) sa[cnt[str[i] - 'a']--] = i;
    for(ll i = 1; i <= n; i++)
        if(str[sa[i]] == str[sa[i - 1]]) rk[sa[i]] = p;
        else rk[sa[i]] = ++p;
    for(ll w = 1; w < n; w <<= 1) {
        memset(cnt, 0, sizeof cnt), p = 0;
        for(ll i = 0; i < w; i++) id[++p] = n - i;
        for(ll i = 1; i <= n; i++)
            if(sa[i] > w) id[++p] = sa[i] - w;
        for(ll i = 1; i <= n; i++) ++cnt[oldrk[i] = rk[i]];
        for(ll i = 1; i <= n; i++) cnt[i] += cnt[i - 1];
        for(ll i = n; i; i--) sa[cnt[rk[id[i]]]--] = id[i];
        p = 0;
        for(ll i = 1; i <= n; i++)
            if(oldrk[sa[i]] == oldrk[sa[i - 1]] && 
             oldrk[sa[i] + w] == oldrk[sa[i - 1] + w]) rk[sa[i]] = p;
            else rk[sa[i]] = ++p;
    }
}

ll tree[maxn];
void tr_add(ll x, ll v) {
    for(; x <= n; x += x & -x) tree[x] += v;
}
ll tr_ask(ll x) {
    ll v = 0;
    for(; x; x -= x & -x) v += tree[x];
    return v;
}

ll x[maxn], y[maxn], ans[maxn];
vector <pir> vec[maxn], sec[maxn];
vector <ll> bin[maxn];
uLL h1[maxn], h2[maxn], pw1[maxn], pw2[maxn];
const uLL b1 = 37, b2 = 131;

bool check(ll l1, ll r1, ll l2, ll r2) {
    return h1[r1] - h1[l1 - 1] * pw1[r1 - l1 + 1] == h1[r2] - h1[l2 - 1] * pw1[r2 - l2 + 1]
     && h2[r1] - h2[l1 - 1] * pw2[r1 - l1 + 1] == h2[r2] - h2[l2 - 1] * pw2[r2 - l2 + 1];
}
ll lcs(ll i, ll j) {
    ll lo = 1, hi = min(i, j);
    while(lo <= hi) {
        ll mid = lo + hi >> 1;
        if(check(i - mid + 1, i, j - mid + 1, j))
            lo = mid + 1;
        else hi = mid - 1;
    } return hi;
}
ll lcp(ll i, ll j) {
    ll lo = 1, hi = n - max(i, j) + 1;
    while(lo <= hi) {
        ll mid = lo + hi >> 1;
        if(check(i, i + mid - 1, j, j + mid - 1))
            lo = mid + 1;
        else hi = mid - 1;
    } return hi;
}

int main() {
    rd(n), rd(n), rd(q);
    scanf("%s", str + 1), SA();
    pw1[0] = pw2[0] = 1;
    for(ll i = 1; i <= n; i++) {
        pw1[i] = pw1[i - 1] * b1, pw2[i] = pw2[i - 1] * b2;
        h1[i] = h1[i - 1] * b1 + str[i] - 'a';
        h2[i] = h2[i - 1] * b2 + str[i] - 'a';
    }
    for(ll i = 1; i <= q; i++) {
        rd(x[i]), rd(y[i]);
        vec[x[i]].pb(mkp(i, 1));
        vec[x[i] + y[i]].pb(mkp(i, -1));
    }
    for(ll i = n; i; i--) {
        for(pir t: vec[i]) {
            ans[t.fi] += t.se * (n - i - tr_ask(rk[x[t.fi]]));
        }
        tr_add(rk[i], 1);
    }
    memset(tree, 0, sizeof tree);
    for(ll i = 1; i * 2 <= n; i++)
        for(ll j = i; j + i <= n; j += i) {
            if(str[j] != str[j + i] || rk[j] > rk[j + i]) continue;
            ll u = lcs(j, j + i), v = lcp(j, j + i);
            ll l = max(j - i + 1, j - u + 1), r = min(j, j + v - i);
            if(l <= r) sec[l].pb(mkp(i, 1)), sec[r + 1].pb(mkp(i, -1));
        }
    for(ll i = 1; i <= q; i++) bin[x[i]].pb(i);
    for(ll i = 1; i <= n; i++) {
        for(pir t: sec[i]) tr_add(t.fi, t.se);
        for(ll j: bin[i]) ans[j] -= tr_ask(y[j]);
    }
    for(ll i = 1; i <= q; i++) printf("%d\n", ans[i]);
    return 0;
}
posted @ 2025-06-18 16:23  Sktn0089  阅读(23)  评论(0)    收藏  举报