题目:
http://poj.org/problem?id=2887
题意:
初始给定一个字符串,然后有两种操作:
I ch p
在第
p
个字符前插入字符
思路:
线段树离线操作,脑洞大开
#include <bits/stdc++.h>
using namespace std;
const int N = 1000000 + 10, INF = 0x3f3f3f3f;
char ori[N], pat[N];
char op[N], ch[N];
int num[N], pos[N], rpos[N];
struct node
{
int l, r, val;
}tr[N*4];
void push_up(int k)
{
tr[k].val = tr[k<<1].val + tr[k<<1|1].val;
}
void build(int l, int r, int k)
{
tr[k].l = l, tr[k].r = r, tr[k].val = 0;
if(l == r)
{
tr[k].val = 1; return;
}
int mid = (l + r) >> 1;
build(l, mid, k << 1);
build(mid + 1, r, k << 1|1);
push_up(k);
}
void update(int x, int val, int k)
{
if(tr[k].l == tr[k].r)
{
tr[k].val = val; return;
}
int mid = (tr[k].l + tr[k].r) >> 1;
if(x <= mid) update(x, val, k << 1);
else update(x, val, k << 1|1);
push_up(k);
}
int query(int x, int k)
{
if(tr[k].l == tr[k].r) return tr[k].l;
if(x <= tr[k<<1].val) return query(x, k << 1);
else return query(x - tr[k<<1].val, k << 1|1);
}
int main()
{
int m;
scanf("%s", ori);
int len = strlen(ori);
scanf("%d", &m);
num[0] = len;
for(int i = 1; i <= m; i++)
{
scanf(" %c", &op[i]);
if(op[i] == 'I')
{
scanf(" %c%d", &ch[i], &pos[i]);
num[i] = num[i-1] + 1;
}
else scanf("%d", &pos[i]), num[i] = num[i-1];
}
int n = num[m];
build(1, n, 1);
for(int i = m; i >= 1; i--) //从后往前开始插入到线段树,从前往后的话,后插入的状态影响之前的状态,造成位置无法确定
if(op[i] == 'I')
{
pos[i] = min(pos[i], num[i]);
rpos[i] = query(pos[i], 1);//真正要插入的位置
update(rpos[i], 0, 1); //消除当前状态对之后插入状态的影响
pat[rpos[i]] = ch[i];
}
int idx = 0;
for(int i = 1; i <= n; i++) //把原串补进去
if(pat[i] == '\0') pat[i] = ori[idx++];
for(int i = 1; i <= m; i++)
{
if(op[i] == 'Q')
{
int ans = query(pos[i], 1);//查询位置
printf("%c\n", pat[ans]);
}
else update(rpos[i], 1, 1);
}
return 0;
}