BZOJ 1014 火星人 | 平衡树维护哈希

BZOJ 1014 火星人

题意

有一个字符串,三中操作:在某位置后面插入一个字符、修改某位置的字符、询问两个后缀的最长公共前缀。

题解

看到网上的dalao们都说这道题是平衡树,我就很懵x……平衡树维护什么啊?
最后发现某个节点维护的是它所代表的区间的哈希值——显然这个哈希值可以从左右子树的哈希值和这个节点上的字符算出来。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;
typedef unsigned long long ll;
template <class T>
void read(T &x){
    char c;
    bool op = 0;
    while(c = getchar(), c < '0' || c > '9')
    if(c == '-') op = 1;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
    x = x * 10 + c - '0';
    if(op) x = -x;
}
template <class T>
void write(T x){
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}
const int N = 100005, B = 29;
int n, m;
int idx, root, fa[N], ls[N], rs[N], val[N], sze[N];
ll hsh[N], pw[N];
char s[N];
#define which(x) (ls[fa[(x)]] == (x))
void upt(int u){
    sze[u] = sze[ls[u]] + sze[rs[u]] + 1;
    hsh[u] = hsh[ls[u]] + val[u] * pw[sze[ls[u]]] + hsh[rs[u]] * pw[sze[ls[u]] + 1];
}
void rotate(int u){
    int v = fa[u], w = fa[v], b = which(u) ? rs[u] : ls[u];
    if(w) which(v) ? ls[w] = u : rs[w] = u;
    which(u) ? (ls[v] = b, rs[u] = v) : (rs[v] = b, ls[u] = v);
    fa[u] = w, fa[v] = u;
    if(b) fa[b] = v;
    upt(v), upt(u);
}
void splay(int u, int tar){
    while(fa[u] != tar){
        if(fa[fa[u]] != tar){
            if(which(u) == which(fa[u])) rotate(fa[u]);
            else rotate(u);
        }
        rotate(u);
    }
    if(!tar) root = u;
}
int build(int l, int r, int pre){
    if(l > r) return 0;
    int u = ++idx, mid = (l + r) >> 1;
    val[u] = s[mid] - 'a' + 1, fa[u] = pre;
    ls[u] = build(l, mid - 1, u);
    rs[u] = build(mid + 1, r, u);
    upt(u);
    return u;
}
int find(int x){
    int u = root;
    while(sze[ls[u]] != x)
        if(x <= sze[ls[u]] - 1) u = ls[u];
        else x -= sze[ls[u]] + 1, u = rs[u];
    return u;
}
void insert(int pos, int x){
    int u = find(pos), v = find(pos + 1);
    splay(u, 0), splay(v, u);
    ls[v] = ++idx, fa[idx] = v, val[idx] = x, sze[idx] = 1;
    splay(idx, 0);
}
void change(int pos, int x){
    int u = find(pos);
    val[u] = x;
    splay(u, 0);
}
ll gethsh(int pos, int len){
    int u = find(pos - 1), v = find(pos + len);
    splay(u, 0), splay(v, u);
    return hsh[ls[v]];
}
int query(int a, int b){
    int l = 0, r = sze[root] - max(a, b) - 1, mid;
    while(l < r){
        mid = (l + r + 1) >> 1;
        if(gethsh(a, mid) == gethsh(b, mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}
int main(){
    scanf("%s", s + 1);
    n = strlen(s + 1);
    pw[0] = 1;
    for(int i = 1; i < N; i++)
        pw[i] = pw[i - 1] * B;
    root = build(0, n + 1, 0);
    read(m);
    while(m--){
        char op[2];
        scanf("%s", op);
        if(op[0] == 'Q'){
            int a, b;
            read(a), read(b);
            write(query(a, b)), enter;
        }
        else if(op[0] == 'I'){
            int pos;
            scanf("%d%s", &pos, op);
            insert(pos, op[0] - 'a' + 1);
        }
        else if(op[0] == 'R'){
            int pos;
            scanf("%d%s", &pos, op);
            change(pos, op[0] - 'a' + 1);
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/RabbitHu/p/BZOJ1014.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值