UVa1480/LA5034 Jewel

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;
}
相关推荐
ReineRabbit6 天前
重生之 CF2126G2. Big Wins! (hard version)
线段树·codeforces·中位数
惆怅客1232 个月前
UVa12298 3KP-BASH Project
模拟·icpc·uva
keysky2 个月前
「ABC 406 G」Travelling Salesman Problem
线段树·slope trick·数形结合
swan4162 个月前
2025GDCPC广东省赛游记(附赛时代码)
c++·算法·acm·icpc·算法竞赛
pystraf3 个月前
LG P9844 [ICPC 2021 Nanjing R] Paimon Segment Tree Solution
数据结构·c++·算法·线段树·洛谷
pystraf3 个月前
P2572 [SCOI2010] 序列操作 Solution
数据结构·算法·线段树·洛谷
所以遗憾是什么呢?3 个月前
【数论分块】数论分块算法模板及真题
数据结构·算法·acm·icpc·数论分块
pystraf3 个月前
UOJ 228 基础数据结构练习题 Solution
数据结构·c++·算法·线段树
GEEK零零七4 个月前
Leetcode 2158. 每天绘制新区域的数量【Plus题】
算法·leetcode·线段树·并查集