【WC2016模拟】String

本文介绍了一种结合后缀数组、Cdq分治及线段树的数据结构算法,用于解决特定类型的问题。该算法通过预处理字符串并利用线段树维护区间信息,有效地解决了跨区间查询的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description:

这里写图片描述

题解:

一眼就是在后缀树上乱搞。

仔细思考可以用dsu on tree+线段树来维护,常数巨小。

nilil说用后缀数组+Cdq分治+线段树。

大概是这样:
对于一个区间[x..y],设m=(x+y)/2

那要求跨过m的两个点的答案。

可以设l,r。

l往左边扫过去,得到一个到终点的height的min值。

同时r往右边调。使r到m的height的min值小于等于l的。

反着做一遍。

r往右边扫,同时l往右边调,是l的min值小于r的。

这样就做完了所有的情况。

calc的部分较繁琐,推导过程略。

Code:

#pragma GCC optimize(2)
#include<cstdio> 
#include<cstring>
#include<algorithm>
#define ll long long
#define ul unsigned long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;

const ll mo = 4294967296;

const int N = 1e5 + 5;

char s[N]; int n, m;
int le[N], SA[N], rank[N], tp[N], tax[N], he[N];

void Rsort() {
    fo(i, 1, m) tax[i] = 0;
    fo(i, 1, n) tax[rank[tp[i]]] ++;
    fo(i, 1, m) tax[i] += tax[i - 1];
    fd(i, n, 1) SA[tax[rank[tp[i]]] --] = tp[i];
}
int cmp(int *f, int x, int y, int w) {return f[x] == f[y] && f[x + w] == f[y + w];}
void Suffix_array() {
    fo(i, 1, n) rank[i] = s[i], tp[i] = i;
    m = 127; Rsort();
    for(int p = 1, w = 1; p < n; w <<= 1, m = p) {
        p = 0; fo(i, n - w + 1, n) tp[++ p] = i;
        fo(i, 1, n) if(SA[i] > w) tp[++ p] = SA[i] - w;
        Rsort(), swap(rank, tp), rank[SA[1]] = p = 1;
        fo(i, 2, n) rank[SA[i]] = cmp(tp, SA[i], SA[i - 1], w) ? p : ++ p;
    }
    int j, k = 0;
    for(int i = 1; i <= n; he[rank[i ++]] = k)
        for(k = k ? k - 1 : 0, j = SA[rank[i] - 1]; s[i + k] == s[j + k]; k ++);
    fo(i, 1, n) le[i] = n - SA[i] + 1;
}

struct tree {
    int a, b;
    ul g, s;
} t[N * 4];
void Clear(int i) { t[i].a = 1; t[i].b = t[i].g = t[i].s = 0; }
void ad(int i, int x, int y, int c) {t[i].b += c, t[i].g += (ll) (y - x + 1) * c, t[i].s += (ll) ((x + y) * (y - x + 1) / 2) % mo * c;}
void down(int i, int x, int y) {
    int m = x + y >> 1;
    if(t[i].a) { Clear(i + i); Clear(i + i + 1); t[i].a = 0;}
    if(t[i].b) { ad(i + i, x, m, t[i].b); ad(i + i + 1, m + 1, y, t[i].b); t[i].b = 0;}
}
void add(int i, int x, int y, int l, int r , int c) {
    if(x == l && y == r) ad(i, x, y, c); else {
        int m = x + y >> 1; down(i, x, y);
        if(r <= m) add(i + i, x, m, l, r, c); else
        if(l > m) add(i + i + 1, m + 1, y, l, r, c); else
        add(i + i, x, m, l, m, c), add(i + i + 1, m + 1, y, m + 1, r, c);
        t[i].g = t[i + i].g + t[i + i + 1].g;
        t[i].s = t[i + i].s + t[i + i + 1].s;
    }
}
ul fg(int i, int x, int y, int l, int r) {
    if(l > r) return 0; if(l < 1) l = 1; if(r > n) r = n;
    if(x == l && y == r) return t[i].g;
    int m = x + y >> 1; down(i, x, y);
    if(r <= m) return fg(i + i, x, m, l, r);
    if(l > m) return fg(i + i + 1, m + 1, y, l, r);
    return fg(i + i, x, m, l, m) + fg(i + i + 1, m + 1, y, m + 1, r);
}
ul fs(int i, int x, int y, int l, int r) {
    if(l > r) return 0; if(l < 1) l = 1; if(r > n) r = n;
    if(x == l && y == r) return t[i].s;
    int m = x + y >> 1; down(i, x, y);
    if(r <= m) return fs(i + i, x, m, l, r);
    if(l > m) return fs(i + i + 1, m + 1, y, l, r);
    return fs(i + i, x, m, l, m) + fs(i + i + 1, m + 1, y, m + 1, r);
}
ul gg(int m, int p) {
    if(m < 0) return 0;
    ul s = fg(1, 1, n, p, p + m) * (m + p + 1) - fs(1, 1, n, p, p + m);
    s += fg(1, 1, n, p - m, p - 1) * (m - p + 1) + fs(1, 1, n, p - m, p - 1);
    return s;
}
ul l, ans;
void Calc(int p, int m) {
    ans += fg(1, 1, n, l + 1, p + l) * (p + l + 1) - fs(1, 1, n, l + 1, p + l);
    ans += fg(1, 1, n, 1, l) * p;
    ans -= fg(1, 1, n, 1, p - l - 1) * (p - l) - fs(1, 1, n, 1, p - l - 1);
    ans += gg(l - 1, p)- gg(l - min(l, m - p) - 1, p);
}
void dg(int x, int y) {
    if(x == y) {
        x = le[x];
        y = min(x - 1, l);
        ans += (ll) (y + 1) * x - y * (y + 1) / 2;
        return;
    }
    int m = x + y >> 1;
    dg(x, m); dg(m + 1, y);

    Clear(1); add(1, 1, n, 1, le[m], 1);
    int p = le[m];
    fo(i, m + 1, y) p = min(p, he[i]), Calc(p, le[i]);
    Clear(1);
    int u = m - 1, v = m + 1; p = he[m];
    for(; u >= x; u --) {
        while(v <= y && he[v] >= p)
            add(1, 1, n, 1, le[v], 1), v ++;
        Calc(p, le[u]);
        p = min(p, he[u]);
    }
    Clear(1);
    u = m; v = m + 1; p = he[v];
    for(; v <= y; v ++) {
        p = min(p, he[v]);
        while(u > x && he[u] > p)
            u --, add(1, 1, n, 1, le[u], 1);
        Calc(p, le[v]);
    }
}
int main() {
    scanf("%s", s + 1); n = strlen(s + 1);
    scanf("%lld", &l);
    Suffix_array();
    dg(1, n);
    printf("%lld\n", ans % mo);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值