UVA 11996 Jewel Magic

UVA_11996
    这个题目前三个操作都是典型的splay的操作,但是第四个操作之前没见过,我以前只用字典树+二分求过任意两个串的LCP,但这次是在splay上便一时间无从下手。后来看了别人的代码发现也是基于二分的,而至于如何判断二分出来的前缀是相同的,则直接用哈希值去比较了。

    至于直接用哈希值判断两个字符串是否相等,我一开始也觉得很不靠谱,后来分析了一下发现虽然不靠谱但不靠谱的概率是比较小的。假定我们用一个long long的哈希值,它可以区分出2^64个不同的字符串(当然这只是极限情况,如果哈希算法不同所能表示的字符串的数量也不大相同),而题目的01串至多也就400,000位,这样即便每个子串都不同也就才有1.6*10^11个,是远远小于2^64的,因此不同的子串出现同一个哈希值的概率微乎其微。

    为了让同样的字符串能得到同样的哈希值,可以用对应位的值乘以x的对应次幂来计算哈希值,x选3、选7都能AC,至于选别的我没试过,但是一定别选2之类的,因为用long long存x的幂相当于自动mod2^64,这样后面的值就都是0了……在维护splay的同时维护好当前节点所表示的区间的哈希值就可以了,由于涉及到翻转的操作,可以选择维护两个哈希值,一个是正着算的,一个是倒着算的,当一个区间翻转时只要将这两个哈希值交换就可以了,而不必再重新计算。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define MAXD 400010
typedef long long LL;
int N, M, T, node;
LL fac[MAXD];
struct Splay
{
    int pre, ls, rs, key, size, rev;
    LL h[2];
    void pushdown(); void update(); void zig(int x); void zag(int x); void splay(int x, int goal);
    void init()
    {
        pre = ls = rs = key = size = rev = h[0] = h[1] = 0;    
    }
}sp[MAXD];
inline void Rev(int cur)
{
    if(cur) sp[cur].rev ^= 1, std::swap(sp[cur].h[0], sp[cur].h[1]);
}
inline void Splay::pushdown()
{
    if(rev)
    {
        Rev(ls), Rev(rs);
        std::swap(ls, rs);
        rev = 0;
    }
}
inline void Splay::update()
{
    size = sp[ls].size + sp[rs].size + 1;
    h[0] = sp[ls].h[0] * fac[sp[rs].size + 1] + key * fac[sp[rs].size] + sp[rs].h[0];
    h[1] = sp[rs].h[1] * fac[sp[ls].size + 1] + key * fac[sp[ls].size] + sp[ls].h[1];
}
void Splay::zig(int x)
{
    int y = rs, fa = pre;
    rs = sp[y].ls, sp[rs].pre = x;
    sp[y].ls = x, pre = y;
    sp[y].pre = fa;
    if(fa == 0) T = y;
    else sp[fa].rs == x ? sp[fa].rs = y : sp[fa].ls = y;
    update();
}
void Splay::zag(int x)
{
    int y = ls, fa = pre;
    ls = sp[y].rs, sp[ls].pre = x;
    sp[y].rs = x, pre = y;
    sp[y].pre = fa;
    if(fa == 0) T = y;
    else sp[fa].rs == x ? sp[fa].rs = y : sp[fa].ls = y;
    update();
}
void Splay::splay(int x, int goal)
{
    int y, z;
    for(; pre != goal;)
    {
        y = pre;
        if(sp[y].pre == goal) sp[y].rs == x ? sp[y].zig(y) : sp[y].zag(y);
        else
        {
            z = sp[y].pre;
            if(sp[z].rs == y)
            {
                if(sp[y].rs == x) sp[z].zig(z), sp[y].zig(y);
                else sp[y].zag(y), sp[z].zig(z);    
            }
            else
            {
                if(sp[y].ls == x) sp[z].zag(z), sp[y].zag(y);
                else sp[y].zig(y), sp[z].zag(z);
            }    
        }
    }
    update();
}
void Goto(int k, int goal)
{
    int i = T, n;
    for(;;)
    {
        sp[i].pushdown();
        n = sp[sp[i].ls].size;
        if(n + 1 == k)
            break;
        else if(k <= n) i = sp[i].ls;
        else i = sp[i].rs, k -= n + 1;
    }
    sp[i].splay(i, goal);
}
char b[MAXD];
void build(int &cur, int x, int y, int fa)
{
    int mid = x + y >> 1;
    sp[mid].init();
    cur = mid, sp[mid].pre = fa, sp[mid].key = b[mid] - '0';
    if(x < mid) build(sp[mid].ls, x, mid - 1, mid);
    if(mid < y) build(sp[mid].rs, mid + 1, y, mid);
    sp[mid].update();
}
void init()
{
    scanf("%s", b + 1);
    sp[0].init(), sp[N + 1].init(), sp[N + 2].init();
    T = N + 1, sp[N + 1].rs = N + 2, sp[N + 2].pre = N + 1;
    build(sp[N + 2].ls, 1, N, N + 2);
    sp[N + 2].update(), sp[N + 1].update();
    node = N + 2;
}
void newnode(int &cur, int c)
{
    cur = ++ node;
    sp[cur].init(), sp[cur].key = c;
    sp[cur].update();
}
void insert(int x, int c)
{
    Goto(x + 1, 0), Goto(x + 2, T);
    int cur = sp[T].rs;
    newnode(sp[cur].ls, c);
    sp[node].pre = cur;
    sp[cur].update(), sp[T].update();
}
void remove(int x)
{
    Goto(x, 0), Goto(x + 2, T);
    int cur = sp[T].rs;
    sp[cur].ls = 0;
    sp[cur].update(), sp[T].update();    
}
void reverse(int x, int y)
{
    Goto(x, 0), Goto(y + 2, T);
    Rev(sp[sp[T].rs].ls);
}
int hash(int x, int y)
{
    Goto(x, 0), Goto(y + 2, T);
    return sp[sp[sp[T].rs].ls].h[0];
}
int common(int x, int y, int L)
{
    return hash(x, x + L - 1) == hash(y, y + L - 1);
}
void LCP(int x, int y)
{
    int min, mid, max, L = sp[T].size - 2;
    min = 0, max = std::min(L - x + 1, L - y + 1) + 1;
    for(;;)
    {
        mid = min + max >> 1;
        if(mid == min) break;
        if(common(x, y, mid)) min = mid;
        else max = mid;    
    }
    printf("%d\n", mid);
}
void solve()
{
    int i, op, x, y;
    for(i = 0; i < M; i ++)
    {
        scanf("%d", &op);
        if(op == 1)
        {
            scanf("%d%d", &x, &y);
            insert(x, y);
        }
        else if(op == 2)
        {
            scanf("%d", &x);
            remove(x);
        }
        else if(op == 3)
        {
            scanf("%d%d", &x, &y);
            reverse(x, y);
        }
        else
        {
            scanf("%d%d", &x, &y);
            LCP(x, y);
        }
    }
}
int main()
{
    fac[0] = 1;
    for(int i = 1; i < MAXD; i ++) fac[i] = 7 * fac[i - 1]; // Choosing 7 is just as my like.
    while(scanf("%d%d", &N, &M) == 2)
    {
        init();
        solve();    
    }
    return 0;    
}
原文地址:https://www.cnblogs.com/staginner/p/2654023.html