UVa1480/LA5034 Jewel
题目链接
本题是2010年icpc亚洲区域赛天津赛区的J题
题意
Jimmy买了很多颗大小互不相同的珠子用来穿成项链送给他的女朋友,初始时项链是空的。Jimmy会进行4种操作:
操作 | 说明 |
---|---|
Insert x | 将尺寸为x的珠子穿到项链的尾部 |
Query_1 s t k | 查询位置在[s,t]区间中第k小珠子的尺寸 |
Query_2 x | 查询当前项链串中尺寸为x的珠子的排名 |
Query_3 k | 查询当前项链串中排名为k的珠子的尺寸 |
输入格式
多组输入,每组数据第一行是一个整数 N ,表示操作的总数,接下来 N 行每行描述各个操作。每组数据 Insert 总数不超过 100000,Query_1、Query_2 和 Query_3 每类的总数都小于 35000。
输出格式
对每组数据输出4行,第一行为"Case T:"(其中T是数据的id),接下来 3 行依次是 Query_1、Query_2 和 Query_3 每类的结果总和。
分析
如果没有Query_1,那么直接用名次树就行了:每次往名次树插入 x 或者查询 kth(k)、rank(x)。为了解决 Query_1 还是要上线段树 ,需要用到"前缀"思想(每个 Insert 操作对应一棵线段树,并且是在前一棵线段树基础上更新得到的),并且按题目交代的数据规模需要动态开点。同名次树一样,线段树每个节点维护其区间内的附加域 size。
把所有 Insert x i x_i xi 操作的 M 个数放到数组里面做一次排序,可以得到每个每个 x i x_i xi 的最终位置 p [ x i ] p[x_i] p[xi],然后再依次处理各个操作:对每个 Insert x i x_i xi 初始化其线段树 s t [ i ] st[i] st[i] 为上一棵线段树 s t [ i − 1 ] st[i-1] st[i−1],然后从线段树根结点到 p [ x i ] p[x_i] p[xi] 所在的叶结点路径上每个结点的 size 做更新(具体来说,根节点 o 对应区间 [1,M],其 size 增加1,记 m i d = ( 1 + M ) / 2 mid = (1+M)/2 mid=(1+M)/2,若 p [ x i ] p[x_i] p[xi] 在 [1,mid] 区间,则递归更新左子树 lc[o] ------ 对应区间 [1,mid],否则递归更新右子树 rc[o] ------ 对应区间 [mid+1,M]);对每个 Query_1 s t k,拿 s t [ s − 1 ] st[s-1] st[s−1] 和 s t [ t ] st[t] st[t] 这两棵线段树做联合查询能得到答案(参见 AC 代码的 kth 方法);对每个 Query_2 x,记当前线段树是第 c 棵, 拿 s t [ c ] st[c] st[c] 做 rank 查询(参见 AC 代码的 rnk 方法);Query_3 k 是 Query_1 的特列(记当前线段树是第 c 棵,则为 Query_1 1 c k)。
AC 代码
cpp
#include <iostream>
#include <algorithm>
using namespace std;
#define N 103000
#define T 18*N
struct {int e, s, t, x;} op[2*N]; int a[N], st[N], lc[T], rc[T], s[T], m, n, t, kase = 0; char e[8];
void update(int& o, int pre, int l, int r, int x) {
int m = (l+r)>>1; o = ++t; lc[o] = lc[pre]; rc[o] = rc[pre]; s[o] = s[pre]+1;
if (l == r) return;
x <= m ? update(lc[o], lc[pre], l, m, x) : update(rc[o], rc[pre], m+1, r, x);
}
int kth(int o, int pre, int l, int r, int k) {
if (l == r) return a[l];
int c = s[lc[o]] - s[lc[pre]], m = (l+r) >> 1;
return k <= c ? kth(lc[o], lc[pre], l, m, k) : kth(rc[o], rc[pre], m+1, r, k-c);
}
int rnk(int o, int l, int r, int x) {
if (l == r) return 1;
int m = (l+r)>>1;
return x <= m ? rnk(lc[o], l, m, x) : s[lc[o]] + rnk(rc[o], m+1, r, x);
}
void solve() {
for (int i=m=t=0; i<n; ++i) {
cin >> e; op[i].e = e[0]=='I' ? 0 : e[6]-'0';
op[i].e == 1 ? cin >> op[i].s >> op[i].t >> op[i].x : cin >> op[i].x;
if (op[i].e == 0) a[++m] = op[i].x;
}
sort(a+1, a+m+1);
long long q1 = 0, q2 = 0, q3 = 0;
for (int i=0, c=0; i<n; ++i) {
if (op[i].e == 0) ++c, update(st[c], st[c-1], 1, m, lower_bound(a+1, a+m+1, op[i].x)-a);
else if (op[i].e == 1) q1 += kth(st[op[i].t], st[op[i].s-1], 1, m, op[i].x);
else if (op[i].e == 2) q2 += rnk(st[c], 1, m, lower_bound(a+1, a+m+1, op[i].x)-a);
else q3 += kth(st[c], st[0], 1, m, op[i].x);
}
cout << "Case " << ++kase << ':' << endl << q1 << endl << q2 << endl << q3 << endl;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
st[0] = lc[0] = rc[0] = s[0] = 0;
while (cin >> n) solve();
return 0;
}