好题 线段树对数据的保存+离线的逆向插入 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", 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\n", tval[p]);
        }
    }
    return 0;
}

时间: 2024-10-10 19:37:27

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

经典算法题每日演练——第十二题 线段树

原文:经典算法题每日演练--第十二题 线段树 这一篇我们来看树状数组的加强版线段树,树状数组能玩的线段树一样可以玩,而且能玩的更好,他们在区间求和,最大,平均 等经典的RMQ问题上有着对数时间的优越表现. 一:线段树 线段树又称"区间树”,在每个节点上保存一个区间,当然区间的划分采用折半的思想,叶子节点只保存一个值,也叫单元节点,所 以最终的构造就是一个平衡的二叉树,拥有CURD的O(lgN)的时间. 从图中我们可以清楚的看到[0-10]被划分成线段的在树中的分布情况,针对区间[0-N],最多有

hihoCoder #1079 : 离散化 (线段树,数据离散化)

题意:有一块宣传栏,高一定,给出长度,再给出多张海报的张贴位置,问还能见到几张海报(哪怕有一点被看到)?假设海报的高于宣传栏同高. 思路:问题转成“给出x轴上长为L的一条线段,再用n条线段进行覆盖上去,最后还能看到及条线”.长度是0~L,即长度是L,进行离散化的时候,应该用1~L,每个数字表示一个单位长.还有就是按照提示所给的信息实现即可.步骤如下: (1)保存n个数据,做成pair,并将所有出现过的数字在另外找地方排序,去掉重复的,再将数据紧缩化处理,那么大小在1~max.再将紧缩化的数据与原

hdu 5475 模拟计算器乘除 (2015上海网赛F题 线段树)

给出有多少次操作 和MOD 初始值为1 操作1 y 表示乘上y操作2 y 表示除以第 y次操作乘的那个数 线段树的叶子结点i 表示 第i次操作乘的数 将1替换成y遇到操作2 就把第i个结点的值 替换成1利用线段树的性质,对整个1~n的区间进行维护,每次输出sum[1]的值即可 Sample Input110 10000000001 22 11 21 102 32 41 61 71 122 7 Sample OutputCase #1:2122010164250484 1 # include <i

codechef FIBTREE 码农题 线段树 树剖 标记永久化

好烦啊,调了半天 线段树部分标记比较多,手抖打错了一个 剩下的都是取模的问题 我自己瞎jb推的公式里保留了abs,但是在模意义下是gg的,所以必须把正负区分开 调试的时候一定要注意构造各种形状的树,不要只做随机树 随机树深度只有log,很难体现一些链上的性质 我用随机树拍了一下午没出错,一掏出直链就秒秒钟出错 最后找到了那个该死的abs 还是逻辑不够严谨啊 1 #include <bits/stdc++.h> 2 #define DEBUG 0 3 #define mid (l+r>&g

支点旋转(自创题 &amp; 线段树) - xgtao -

Description在一个平面上有N 个首位相连的杠杆.初始所有杠杆都为1 个单位长度.第一根杠杆左侧位于(0,0)处.所有杠杆水平放置. 现在我会对这些杠杆进行以下两种操作:1.拉伸此操作标号为1,意为将某根杠杆沿原放向伸长x 个单位. 2.旋转此标号操作为2,意为将某根杠杆逆时针伸长x 度. 现在,我关心的是每次操作后第N 个杠杆的右侧在哪里呢? Input第一行两个整数N,M 表示杠杆数和操作数.接下来若干行,每行三个数x,y,z.若x=1,表示对第y 根杠杆伸长z 个单位.若x=2,表

hdu 4031 2011成都赛区网络赛A题 线段树 ***

就是不知道时间该怎么处理,想了好久,看了别人的题解发现原来是暴力,暴力也很巧妙啊,想不出来的那种  -_-! 1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<queue> 7 #include<map> 8 using namespace std; 9

笔试算法题(31):将有序数组转换成BST表示 &amp; 线段树的应用

出题:要求将一个有序整数数组转换成最小深度的Binary Search Tree表示: 分析:由于需要是最小深度,所以BST应保持平衡,左右节点数大致相当,并且BST中当前根节点大于所有其左子树中的元素,小于所有其右子树中的元素.对于排序数组而言,中间元素必然作为根节点,然后递归对由中间元素分割的左右数组部分进行处理: 解题: 1 struct Node { 2 int value; 3 Node *left; 4 Node *right; 5 }; 6 7 Node* Array2BST(in

HDU4417 线段树 + 离线处理

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4417 , 线段树(或树状数组) + 离线处理 最近看了几道线段树的题都是需要离线处理数据的,正好这块比较手生,就练练了.  这道题主要的地方就是离线处理数据,具体想法: ① 先把所有位置的高度都存下来,然后排序,注意保存下标: ② 把所有询问存下来,然后按照询问的高度进行排序,同注意保存下标: ③ 对于排序后的每次询问的处理:由于每个位置的高度都已经存了下来并且进行了排序,所以可以按照顺序将每个点插

xdoj1023 IP查询 动态开点的线段树

xdoj1023 IP查询    动态开点的线段树 1023: IP查询 时间限制: 1 Sec  内存限制: 128 MB提交: 3473  解决: 228[提交][状态][讨论版] 题目描述 现实生活中,每一个IP段都指向一座城市.为了简化问题,我们将IP段直接看做一个整形数,每座城市也有自己的唯一标识ID,也可以看做一个整数.那么问题来了,现在已知有多个闭区间代表多个IP段,每个区间对应一个城市的ID.现在,小L要查询某个IP属于那个城市,希望聪明的你来帮他完成. 输入 第一行输入T,表示