最新文章

[APIO2010]特别行动队 题解

[APIO2010]特别行动队 题解

题目地址:洛谷:【P3628】[APIO2010]特别行动队 – 洛谷、BZO 

[HNOI2008]玩具装箱 题解 & DP斜率优化原理与实现

[HNOI2008]玩具装箱 题解 & DP斜率优化原理与实现

题目地址:洛谷:【P3195】[HNOI2008]玩具装箱TOY – 洛谷、B 

[CF314E]Sereja and Squares 题解

[CF314E]Sereja and Squares 题解

题目地址:Codeforces:Problem – 314E – Codeforces、洛谷:【CF314E】Sereja and Squares – 洛谷

题目描述

Sereja painted n points on the plane, point number i (1 ≤ i ≤ n) has coordinates (i, 0). Then Sereja marked each point with a small or large English letter. Sereja don’t like letter “x”, so he didn’t use it to mark points. Sereja thinks that the points are marked beautifully if the following conditions holds:

  • all points can be divided into pairs so that each point will belong to exactly one pair;
  • in each pair the point with the lesser abscissa will be marked with a small English letter and the point with the larger abscissa will be marked with the same large English letter;
  • if we built a square on each pair, the pair’s points will be the square’s opposite points and the segment between them will be the square’s diagonal, then among the resulting squares there won’t be any intersecting or touching ones.

Little Petya erased some small and all large letters marking the points. Now Sereja wonders how many ways are there to return the removed letters so that the points were marked beautifully.
本来有一个由大小写字母组成的序列,序列满足以下条件:

  • 大小写字母两两都配对,配对指同一字母的大小写配对,且小写字母在左侧
  • 不能发现穿插的情况,例如abAB是不合法的,应该调整为aAbBabBA

现在所有的大写字母和部分小写字母被擦掉了,求恢复为一个满足上述序列的可行情况数量。

输入输出格式

输入格式:
The first line contains integer n the number of points (1 ≤ n ≤ 105). The second line contains a sequence consisting of n small English letters and question marks — the sequence of letters, that mark points, in order of increasing x-coordinate of points. Question marks denote the points without letters (Petya erased them). It is guaranteed that the input string doesn’t contain letter “x”.

输出格式:
In a single line print the answer to the problem modulo 4294967296. If there is no way to return the removed letters, print number 0.
Please, do not write the %lld specifier to read or write 64-bit integers in С++. It is preferred to use the cin, cout streams or the %I64d specifier.

输入输出样例

输入样例#1:

4
a???

输出样例#1:

20

输入样例#2:

4
abc?

输出样例#2:

0

输入样例#3:

6
abc???

输出样例#3:

1

题解

首先我们可以把小写字母看成左括号,大写字母看成右括号,这样就变成了求一个合法的括号序列的问题了。我们考虑使用动态规划。
令dp[i][j]表示处理到第i个字符,到现在还有j个左括号没有配对。分类转移:
如果i+1是左括号,把当前值加到dp[i+1][j+1]上。
如果i+1是问号,把当前值加到dp[i+1][j-1](如果i+1是右括号)和dp[i+1][j+1](如果i+1是左括号)上。
最后的答案就是dp[n][0]。
但是这么做的时间复杂度是O(n^2)的。因为i和j的上限都是n。

我们尝试换一种思路,用dp[i][j]表示处理到前i个,里面有j个右括号。只需要转移当前是问号的情况,填左括号dp[i][j]+=dp[i-1][j],填右括号dp[i][j]+=dp[i-1][j-1]。
最后的答案是dp[n][n/2]。
相比前一种想法,在这种想法中里面那层循环的规模会减小很多,这个可以感性的认识一下,因此我们大幅减小了常数,这个题就能过了。

等等,还没完!原题说用[a-wy-z]这个字符集里面的字母当做左括号来着,那么我们还要乘上25^{\frac{n}{2}-q}这个数字。q是题目固定的小写字母数量。因为每个左括号都可以有25种选择,右括号又是已经配对好的。到这里这个题就做完了。
对于取模,我们发现4294967296 = 2^{32},可以使用unsigned int的自然溢出来减小取模的常数。

代码

// Code by KSkun, 2018/3
#include <cstdio>

// variables

const int MAXN = 100005;

int n;
char str[MAXN];
unsigned int dp[MAXN];

// main

int main() {
    scanf("%d%s", &n, str + 1);
    if(n & 1) {
        printf("0");
        return 0;
    }
    dp[0] = 1;
    int cntl = 0, hn = n / 2;
    for(int i = 1; i <= n; i++) {
        if(str[i] == '?') {
            for(int j = i / 2; j && j >= i - hn; j--) {
                dp[j] += dp[j - 1];
            }
        } else {
            cntl++;
        }
    }
    unsigned int ans = dp[hn];
    for(int i = 1; i <= hn - cntl; i++) {
        ans *= 25;
    }
    printf("%u", ans);
    return 0;
}
[CQOI2009]叶子的染色 题解

[CQOI2009]叶子的染色 题解

题目地址:洛谷:【P3155】[CQOI2009]叶子的染色 – 洛谷、BZO 

[中山OI2011]小W的问题 题解

[中山OI2011]小W的问题 题解

题目地址:BZOJ:Problem 2441. — [中山市选2011]小W的 

[ZJOI2012]网络 题解

[ZJOI2012]网络 题解

题目地址:洛谷:【P2173】[ZJOI2012]网络 – 洛谷、BZOJ:Problem 2816. — [ZJOI2012]网络

题目描述

有一个无向图G,每个点有个权值,每条边有一个颜色。这个无向图满足以下两个条件:

  1. 对于任意节点连出去的边中,相同颜色的边不超过两条。
  2. 图中不存在同色的环,同色的环指相同颜色的边构成的环。

在这个图上,你要支持以下三种操作:

  1. 操作0:修改一个节点的权值。
  2. 操作1:修改一条边的颜色。
  3. 操作2:查询由颜色c的边构成的图中,所有可能在节点u到节点v之间的简单路径上的节点的权值的最大值。

输入输出格式

输入格式:
输入文件network.in的第一行包含四个正整数N, M, C, K,其中N为节点个数,M为边数,C为边的颜色数,K为操作数。
接下来N行,每行一个正整数vi,为节点i的权值。
之后M行,每行三个正整数u, v, w,为一条连接节点u和节点v的边,颜色为w。满足1 ≤ u, v ≤ N,0 ≤ w < C,保证u ≠ v,且任意两个节点之间最多存在一条边(无论颜色)。
最后K行,每行表示一个操作。每行的第一个整数k表示操作类型。

  1. k = 0为修改节点权值操作,之后两个正整数x和y,表示将节点x的权值vx修改为y。
  2. k = 1为修改边的颜色操作,之后三个正整数u, v和w,表示将连接节点u和节点v的边的颜色修改为颜色w。满足0 ≤ w < C。
  3. k = 2为查询操作,之后三个正整数c, u和v,表示查询所有可能在节点u到节点v之间的由颜色c构成的简单路径上的节点的权值的最大值。如果不存在u和v之间不存在由颜色c构成的路径,那么输出“-1”。

输出格式:
输出文件network.out包含若干行,每行输出一个对应的信息。

  1. 对于修改节点权值操作,不需要输出信息。
  2. 对于修改边的颜色操作,按以下几类输出:
    (输出满足条件的第一条信息即可,即若同时满足2和3,则只需要输出“Error 1.”。)

    1. 若不存在连接节点u和节点v的边,输出“No such edge.”。
    2. 若修改后不满足条件1,不修改边的颜色,并输出“Error 1.”。
    3. 若修改后不满足条件2,不修改边的颜色,并输出“Error 2.”。
    4. 其他情况,成功修改边的颜色,并输出“Success.”。
  3. 对于查询操作,直接输出一个整数。

输入输出样例

输入样例#1:

4 5 2 7
1
2
3
4
1 2 0
1 3 1
2 3 0
2 4 1
3 4 0
2 0 1 4
1 1 2 1
1 4 3 1
2 0 1 4
1 2 3 1
0 2 5
2 1 1 4

输出样例#1:

4
Success.
Error 2.
-1
Error 1.
5

说明

【数据规模】
对于30%的数据:N ≤ 1000,M ≤ 10000,C ≤ 10,K ≤ 1000。
另有20%的数据:N ≤ 10000,M ≤ 100000,C = 1,K ≤ 100000。
对于100%的数据:N ≤ 10000,M ≤ 100000,C ≤ 10,K ≤ 100000。

题解

乍一看,好像是图上问题,但是我们冷静分析一下,会发现以下语句。

对于任意节点连出去的边中,相同颜色的边不超过两条。

这不就是维护一堆链嘛,用Splay就行啦。
我们建n*c个点,第i+p*c个点对应颜色p的i号点。
对于操作0,将所有x号点splay到根并且修改权值。
对于操作1,用个map<pair<int, int>, int>来存每条边目前是什么颜色,将原来的两个点一个splay到根,然后断开父子关系(split),然后将新点splay到根重新建立父子关系(merge),如果两个splay的根的儿子在同侧,则要翻转其中一个(有可能接上的时候v点在序列的最右端而非接口处)。
异常判断,不存在边即map里查不到,不满足条件1即splay到根后两个儿子都不为空,不满足条件2即两个节点同根。
对于操作2,由于我们知道常规的查询序列需要头尾各建一个空节点便于查询,但是这里一堆链操作好麻烦,我就改成了splay两个端点,从两个端点和子树最大值中取最大值,来代替空节点。先异常判断查一下是否同根,然后常规操作。
这个题调了2天,终于调出来了。第一个没看别人代码的splay2333。(但是你板子就是看别人代码写的哇)

代码

// Code by KSkun, 2018/3
#include <cstdio>
#include <cstring>

#include <algorithm>
#include <map>
#include <stack>
#include <utility> 
typedef std::pair<int, int> PI;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline int readint() {
    register int res = 0, neg = 1;
    char c = fgc();
    while(c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

inline PI mpair(int first, int second) {
    return std::make_pair(first, second);
}

// variables

const int MAXN = 100005;

int n, m, c, k, op, ut, vt, wt, ct;
std::map<PI, int> edges;
int sta[MAXN], stop = 0;

// splay

struct Node {
    int val, mxv;
    bool rev;
    int ch[2], fa;
} tr[MAXN];

inline void update(int p) {
    int lch = tr[p].ch[0], rch = tr[p].ch[1];
    tr[p].mxv = std::max(tr[p].val, std::max(tr[lch].mxv, tr[rch].mxv));
}

inline void pushdown(int p) {
    int lch = tr[p].ch[0], rch = tr[p].ch[1];
    if(tr[p].rev) {
        std::swap(tr[lch].ch[0], tr[lch].ch[1]);
        std::swap(tr[rch].ch[0], tr[rch].ch[1]);
        tr[lch].rev = !tr[lch].rev;
        tr[rch].rev = !tr[rch].rev;
        tr[p].rev = false;
    }
}

inline void pushto(int p) {
    while(tr[p].fa) {
        sta[stop++] = p;
        p = tr[p].fa;
    }
    while(stop > 0) {
        p = sta[--stop];
        pushdown(p);
    }
}

inline bool isleft(int p) {
    return tr[tr[p].fa].ch[0] == p;
}

inline bool haslch(int p) {
    return tr[p].ch[0] != 0;
}

inline bool islch(int p, int q) {
    return tr[p].ch[0] == q;
}

inline void rotate(int p) { // p is child
    bool type = !isleft(p);
    int fa = tr[p].fa, ffa = tr[fa].fa;
    tr[fa].ch[type] = tr[p].ch[!type];
    tr[p].ch[!type] = fa;
    tr[tr[fa].ch[type]].fa = fa;
    if(ffa) tr[ffa].ch[!isleft(fa)] = p;
    tr[p].fa = tr[fa].fa;
    tr[fa].fa = p;
    update(fa);
    update(p);
}

inline void splay(int p, int tar) {
    pushto(p);
    for(int fa; (fa = tr[p].fa) != tar; rotate(p)) {
        if(tr[tr[p].fa].fa != tar) {
            rotate(isleft(fa) == isleft(p) ? fa : p);
        }
    }
}

inline int findrt(int p) {
    while(tr[p].fa) p = tr[p].fa;
    return p;
}

inline void modifyv(int p, int val) {
    int pt;
    for(int i = 0; i < c; i++) {
        pt = p + i * n;
        splay(pt, 0);
        tr[pt].val = val;
        update(pt);
    }
}

inline void split(int p, int q) {
    splay(p, 0);
    splay(q, p);
    tr[p].ch[!isleft(q)] = 0;
    tr[q].fa = 0;
    update(p);
}

inline void merge(int p, int q) {
    splay(p, 0);
    splay(q, 0);
    if(haslch(p) == haslch(q)) {
        tr[q].rev = !tr[q].rev;
        std::swap(tr[q].ch[0], tr[q].ch[1]);
    }
    tr[p].ch[haslch(p)] = q;
    tr[q].fa = p;
    update(p);
}

inline void modifyc(int p, int q, int col) {
    PI pairt = mpair(p, q);
    if(!edges.count(pairt)) {
        printf("No such edge.\n");
        return;
    }
    int ocol = edges[pairt], pt = p + col * n, qt = q + col * n;
    if(ocol == col) {
        printf("Success.\n");
        return;
    }
    splay(pt, 0);
    splay(qt, 0);
    if((tr[pt].ch[0] && tr[pt].ch[1]) || (tr[qt].ch[0] && tr[qt].ch[1])) {
        printf("Error 1.\n");
        return;
    }
    if(findrt(pt) == findrt(qt)) {
        printf("Error 2.\n");
        return;
    }
    int pt1 = p + ocol * n, qt1 = q + ocol * n;
    split(pt1, qt1);
    merge(pt, qt);
    edges[pairt] = edges[mpair(q, p)] = col;
    printf("Success.\n");
}

inline int query(int p, int q) {
    if(p == q) return tr[p].val;
    if(findrt(p) != findrt(q)) {
        return -1;
    }
    splay(p, 0);
    splay(q, p);
    return std::max(std::max(tr[p].val, tr[q].val), tr[tr[q].ch[islch(p, q)]].mxv);
}

int main() {
    n = readint();
    m = readint();
    c = readint();
    k = readint();
    for(int i = 1; i <= n; i++) {
        tr[i].val = tr[i].mxv = readint();
        for(int j = 1; j < c; j++) {
            tr[i + j * n].val = tr[i + j * n].mxv = tr[i].val;
        }
    }
    for(int i = 1; i <= m; i++) {
        ut = readint();
        vt = readint();
        ct = readint();
        edges[mpair(ut, vt)] = ct;
        edges[mpair(vt, ut)] = ct;
        merge(ut + ct * n, vt + ct * n);
    }
    while(k--) {
        op = readint();
        if(op == 0) {
            ut = readint();
            wt = readint();
            modifyv(ut, wt);
        }
        if(op == 1) {
            ut = readint();
            vt = readint();
            wt = readint();
            modifyc(ut, vt, wt);
        }
        if(op == 2) {
            ct = readint();
            ut = readint();
            vt = readint();
            printf("%d\n", query(ut + ct * n, vt + ct * n));
        }
    }
    return 0;
}
[HNOI2011]括号修复 题解

[HNOI2011]括号修复 题解

题目地址:洛谷:【P3215】[HNOI2011]括号修复 – 洛谷、BZOJ 

[NOI2005]维护数列 题解

[NOI2005]维护数列 题解

题目地址:洛谷:【P2042】[NOI2005]维护数列 – 洛谷、BZOJ: 

[JSOI2008]火星人 题解

[JSOI2008]火星人 题解

题目地址:洛谷:【P4036】[JSOI2008]火星人 – 洛谷、BZOJ:Problem 1014. — [JSOI2008]火星人prefix

题目描述

火星人最近研究了一种操作:求一个字串两个后缀的公共前缀。比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:

序号: 1  2  3  4  5  6  7  8  9 10 11 
字符: m  a  d  a  m  i  m  a  d  a  m 

现在,火星人定义了一个函数LCQ(x, y),表示:该字符串中第x个字符开始的字串,与该字符串中第y个字符开始的字串,两个字串的公共前缀的长度。比方说 LCQ(1, 7) = 5, LCQ(2, 10) = 1, LCQ(4, 7) = 0
在研究LCQ函数的过程中,火星人发现了这样的一个关联:如果把该字符串的所有后缀排好序,就可以很快地求出LCQ函数的值;同样,如果求出了LCQ函数的值,也可以很快地将该字符串的后缀排好序。
尽管火星人聪明地找到了求取LCQ函数的快速算法,但不甘心认输的地球人又给火星人出了个难题:在求取LCQ函数的同时,还可以改变字符串本身。具体地说,可以更改字符串中某一个字符的值,也可以在字符串中的某一个位置插入一个字符。地球人想考验一下,在如此复杂的问题中,火星人是否还能够做到很快地求取LCQ函数的值。

输入输出格式

输入格式:
第一行给出初始的字符串。第二行是一个非负整数M,表示操作的个数。接下来的M行,每行描述一个操作。操作有3种,如下所示

  1. 询问。语法:Q x y,x,y均为正整数。功能:计算LCQ(x,y)限制:1<=x,y<=当前字符串长度。
  2. 修改。语法:R x d,x是正整数,d是字符。功能:将字符串中第x个数修改为字符d。限制:x不超过当前字符串长度。
  3. 插入:语法:I x d,x是非负整数,d是字符。功能:在字符串第x个字符之后插入字符d,如果x=0,则在字符串开头插入。限制:x不超过当前字符串长度

输出格式:
对于输入文件中每一个询问操作,你都应该输出对应的答案。一个答案一行。

输入输出样例

输入样例#1:

madamimadam
7
Q 1 7
Q 4 8
Q 10 11
R 3 a
Q 1 7
I 10 a
Q 2 11

输出样例#1:

5
1
0
2
1

说明

1、所有字符串自始至终都只有小写字母构成。
2、M<=150,000
3、字符串长度L自始至终都满足L<=100,000
4、询问操作的个数不超过10,000个。
对于第1,2个数据,字符串长度自始至终都不超过1,000
对于第3,4,5个数据,没有插入操作。

题解

哈希与哈希值的合并

首先我们知道判断两个字符串是否相同可以使用哈希处理,而哈希也可以处理字符串的子串问题,也就是说,它能够支持区间合并。但是如果我们用维护区间的数据结构线段树,插入操作无法实现。所以我们考虑平衡树来维护这个字符串的哈希。
假如有左儿子a_1, \cdots, a_i,树根a_{i+1},右儿子a_{i+2}, \cdots, a_n,分别写出哈希值的式子和拼起来的哈希值。
左儿子:
a_1 \times e^0 + a_2 \times e^1 + \cdots + a_i \times e^{i-1}
右儿子:
a_{i+2} \times e^0 + a_{i+3} \times e^1 + \cdots + a_n \times e^{n-i-2}
拼起来:
a_1 \times e^0 + a_2 \times e^1 + \cdots + a_i \times e^{i-1} + a_{i+1} \times e^i + a_{i+2} \times e^{i+1} + a_{i+3} \times e^{i+2} + \cdots + a_n \times e^{n-1}
观察可知,拼起来的哈希值可以通过左右儿子的哈希值通过以下计算得到
hash_{lch} + a_{mid} \times e^{llen} + hash_{rch} \times e^{llen+1}
其中llen是左儿子对应区间长度。

插入和修改

插入可以把x和x+1都splay上去,类似区间翻转(文艺平衡树:Splay原理与实现 | KSkun’s Blog)那样,只不过此时根的右儿子的左儿子为空,这个位置就是插入的位置,新建节点放进去即可。
修改可以把要修改的点splay到根,然后修改完以后update它。

二分答案

我们虽然没办法直接计算得到答案,但是我们可以二分相同部分的长度,然后再查询相同部分的哈希是否相等。
单次二分的复杂度是O(\log^2 n)的。

卡常

交到BZOJ上被卡了常,后来把两个取模删掉才过。还好进位取得小,不取模也没爆long long。

代码

// Code by KSkun, 2018/3
#include <cstdio>
#include <cstring>

#include <algorithm>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline int readint() {
    register int res = 0, neg = 1;
    char c = fgc(); 
    while (c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while (c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

inline bool isop(char c) {
    return c == 'Q' || c == 'R' || c == 'I';
}

inline char readop() {
    char c;
    while(!isop(c = fgc()));
    return c;
}

inline char readchr() {
    char c;
    while((c = fgc()) < 'a' || c > 'z');
    return c;
}

inline int readstr(char str[], int startpos) {
    char c = '-';
    int siz = startpos;
    while(c < 'a' || c > 'z') c = fgc();
    while(c >= 'a' && c <= 'z') {
        str[siz++] = c;
        c = fgc();
    }
    str[siz] = '\0';
    return siz - startpos;
}

// variables

const int MAXN = 150005, MO = 1e9 + 7, E = 27;

LL ep[MAXN];
int n, m, x, y;
char str[MAXN], op, d;

inline void init() {
    ep[0] = 1;
    for(int i = 1; i <= 150000; i++) {
        ep[i] = ep[i - 1] * E % MO;
    }
}

// splay

struct Node {
    LL val, chr;
    int ch[2], siz, cnt, fa;
} tr[MAXN]; 
int tot = 0, sta[MAXN], stop = 0, rt = 0;

inline void update(int p) {
    int lch = tr[p].ch[0], rch = tr[p].ch[1];
    tr[p].siz = tr[lch].siz + tr[rch].siz + 1;
    tr[p].val = (tr[lch].val + tr[p].chr * ep[tr[lch].siz] + tr[rch].val * ep[tr[lch].siz + 1]) % MO;
}

inline int newnode() {
    int p;
    if(stop > 0) {
        p = sta[--stop];
    } else {
        p = ++tot;
    } 
    memset(tr + p, 0, sizeof(Node));
    return p;
}

inline void delnode(int a) {
    sta[stop++] = a;
}

inline bool isleft(int p) {
    return tr[tr[p].fa].ch[0] == p;
}

inline void rotate(int p) { // p is child
    bool type = !isleft(p);
    int fa = tr[p].fa, ffa = tr[fa].fa;
    tr[fa].ch[type] = tr[p].ch[!type];
    tr[p].ch[!type] = fa;
    tr[tr[fa].ch[type]].fa = fa;
    if(ffa) tr[ffa].ch[!isleft(fa)] = p;
    tr[p].fa = tr[fa].fa;
    tr[fa].fa = p;
    update(fa);
    update(p);
    if(!tr[p].fa) rt = p;
}

inline void splay(int p, int tar) {
    for(int fa; (fa = tr[p].fa) != tar; rotate(p)) {
        if(tr[tr[p].fa].fa != tar) {
            rotate(isleft(fa) == isleft(p) ? fa : p);
        }
    }
    if(!tar) rt = p;
}

inline int find(int p, int rk) {
    int lch = tr[p].ch[0], rch = tr[p].ch[1];
    if(tr[lch].siz + 1 == rk) return p;
    else if(tr[lch].siz >= rk) return find(lch, rk);
    else return find(rch, rk - tr[lch].siz - 1);
}

inline void insert(int x, int val) {
    int a = find(rt, x + 1), b = find(rt, x + 2);
    splay(a, 0);
    splay(b, a);
    int p = newnode();
    tr[b].ch[0] = p;
    tr[p].fa = b;
    tr[p].chr = val;
    update(p);
    update(b);
    update(a);
}

inline LL query(int x, int len) {
    int a = find(rt, x), b = find(rt, x + len + 1);
    splay(a, 0);
    splay(b, a);
    return tr[tr[b].ch[0]].val;
}

inline int build(int fa, int l, int r) {
    if(l > r) return 0;
    int p = newnode();
    if(!fa) rt = p;
    if(l == r) {
        tr[p].val = tr[p].chr = str[l] - 'a' + 1;
        tr[p].fa = fa;
        tr[p].siz = 1;
        return p;
    }
    int mid = (l + r) >> 1;
    tr[p].ch[0] = build(p, l, mid - 1);
    tr[p].ch[1] = build(p, mid + 1, r);
    tr[p].chr = str[mid] - 'a' + 1;
    tr[p].fa = fa;
    update(p);
    return p;
}

inline int work(int x, int y) {
    int l = 0, r = std::min(n - x, n - y), mid;
    while(r - l > 1) {
        mid = (l + r) >> 1;
        if(query(x, mid) == query(y, mid)) {
            l = mid;
        } else {
            r = mid;
        }
    }
    return l;
}

int main() {
    n = readstr(str, 2) + 2;
    init();
    build(0, 1, n);
    m = readint();
    while(m--) {
        op = readop();
        x = readint();
        if(op == 'Q') {
            y = readint();
            printf("%d\n", work(x, y));
        }
        if(op == 'R') {
            d = readchr();
            int t = find(rt, x + 1);
            splay(t, 0);
            tr[rt].chr = d - 'a' + 1;
            update(rt);
        }
        if(op == 'I') {
            d = readchr();
            insert(x, d - 'a' + 1);
            n++;
        }
    }
    return 0;
}
Splay原理与实现

Splay原理与实现

注:本文部分图片来自互联网,其相关权利归原作者所有,感谢原作者的分享。 概述 Splay(