好题 线段树对数据的保存+离线的逆向插入 POJ 2887

题目大意:给一个字符串,有插入和询问操作,每次往一个位置插入一个字符或者询问第p个位置的字符是什么。

思路:我们离线询问,逆向把所有的字符都插入给线段树,然后再查询就好了,每次都要记得插入线段树的最后的位置,然后要把这个位置给保存下来在O(1)查询即可。

//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
const int maxq = 2000 + 5;
const int maxch = 1002000 + 5;
int tree[maxch << 2];

inline void pushup(int o){
    tree[o] = tree[o << 1] + tree[o << 1 | 1];
}

void buildtree(int o, int l, int r){
    if (l == r){
        tree[o] = 1;
        return ;
    }
    int mid = (l + r) / 2;
    if (l <= mid) buildtree(o << 1, l ,mid);
    if (r > mid) buildtree(o << 1 | 1, mid + 1, r);
    pushup(o);
}

void update(int o, int l, int r, int pos, int val){
    if (l == r && l == pos){
        tree[o] = val;
        return ;
    }
    int mid = (l + r) / 2;
    if (pos <= mid) update(o << 1, l, mid, pos, val);
    if (pos > mid) update(o << 1 | 1, mid + 1, r, pos, val);
    pushup(o);
}

int query(int o, int l, int r, int pos){
    if (l == r) return l;
    int mid = (l + r) / 2;
    if (tree[o << 1] >= pos) return query(o << 1, l, mid, pos);
    if (tree[o << 1] < pos) return query(o << 1 | 1, mid + 1, r, pos - tree[o << 1]);
}
///要知道的东西:询问,初始的ch,线段树节点上的val,线段树该节点下还有多少子节点
struct Query{
    int ty, pos, cnt;///表示目前有几个插入的
    char val;
    Query(int ty = 0, char val = 0, int pos = 0, int cnt = 0): ty(ty), val(val), pos(pos), cnt(cnt){}
}q[maxch];
int tpos[maxch], Q;
char tval[maxch], ch[maxch];

int main(){
    scanf("%s", ch);
    int len = strlen(ch);
    scanf("%d", &Q);
    int cnt = len;
    for (int i = 1; i <= Q; i++){
        char c[2]; scanf("%s", c);
        if (c[0] == 'I') {
            char cc[2]; scanf("%s", cc);
            int pos; scanf("%d", &pos);
            q[i] = Query(c[0], cc[0], pos, cnt + 1);///目前该询问下的种类,val和位置,加上已经有了多少个字母了
            cnt++;
        }
        else {
            int pos; scanf("%d", &pos);
            q[i] = Query(c[0], 0, pos, cnt);
        }
    }
    /*①逆序放入②得到逆序放入以后放入位置的val是多少*/
    int n = cnt;
    ///printf("n = %d
", n);
    buildtree(1, 1, n);

    for (int i = Q; i >= 1; i--){///知道他是在哪个位置的
        if (q[i].ty == 'I'){
            q[i].pos = min(q[i].pos, q[i].cnt);
            int p = query(1, 1, n, q[i].pos); ///线段树里面的位置
            tpos[i] = p;
            tval[p] = q[i].val;///线段树该位置下的val
            update(1, 1, n, p, 0);
        }
    }
    for (int i = 1, j = 0; i <= n; i++){
        if (tval[i] == 0){
            tval[i] = ch[j]; j++;
        }
    }
    for (int i = 1; i <= Q; i++){
        if (q[i].ty == 'I'){
            update(1, 1, n, tpos[i], 1);
        }
        else {
            int p = query(1, 1, n, q[i].pos);
            printf("%c
", tval[p]);
        }
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/heimao5027/p/5874522.html