LuoguP2161 [SHOI2009]会场预约

题目地址

题目链接

题解

用fhqtreap对区间进行维护。

可以注意到的是,对于当前存在的预约,他们一定是升序排列的(有重叠的都被删了)。

那么就可以用按照位置分裂的fhqtreap搞了(预约无论按l还是按r都必定是升序的)。

每次插入一个区间的时候,就直接找出互不重叠的前驱和后继,那么需要删除的就是中间的那一段了(因为区间升序),对于每一个A的答案就是中间那段的siz,合并的时候注意一下顺序就好,别把顺序颠倒了(无论是merge还是split)。

对于B询问,答案就是root的siz。

#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <ctime>
#define ll long long
using namespace std;

const int N = 200010;

struct task { int l, r; };
struct fhq { int l, r, siz, rnd; task v; }t[N];
int tot, root;

#define lc (t[rt].l)
#define rc (t[rt].r)
void up(int rt) { t[rt].siz = t[lc].siz + t[rc].siz + 1; }
void split(int rt, int &l, int &r, int k) {
    if(!k) l = 0, r = rt;
    else if(t[rt].siz == k) l = rt, r = 0;
    else if(k <= t[lc].siz) r = rt, split(lc, l, lc, k), up(rt);
    else l = rt, split(rc, rc, r, k - t[lc].siz - 1), up(rt);
}
void merge(int &rt, int l, int r) {
    if(!l || !r) rt = l + r;
    else if(t[l].rnd < t[r].rnd) rt = l, merge(rc, rc, r), up(rt);
    else rt = r, merge(lc, l, lc), up(rt);
}
int pre(int rt, task a) {
    if(!rt) return 0;
    if(t[rt].v.r < a.l) return pre(rc, a) + t[lc].siz + 1;
    else return pre(lc, a);
}
int nxt(int rt, task a) {
    if(!rt) return 0;
    if(t[rt].v.l > a.r) return nxt(lc, a);
    else return nxt(rc, a) + t[lc].siz + 1;
}
int new_node(task a) {
    t[++tot].v = a;
    t[tot].siz = 1;
    t[tot].rnd = rand()<<15|rand();
    t[tot].l = t[tot].r = 0;
    return tot;
}
int build(task a) {
    int l = pre(root, a), r = nxt(root, a), x, y, z, k;
    split(root, x, y, r); split(x, z, k, l);
    int ans = t[k].siz, t = new_node(a);
    merge(t, z, t); merge(root, t, y); return ans;
}
#undef lc
#undef rc

int main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
freopen("t.out","w",stdout);
#endif
    srand((unsigned)time(0));
    int T; task a; char ch[10];
    scanf("%d", &T);
    while(T--) {
        scanf("%s", ch);
        if(ch[0] == 'A') scanf("%d%d", &a.l, &a.r), printf("%d\n", build(a));
        else printf("%d\n", t[root].siz);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/henry-1202/p/10465543.html

时间: 2024-11-08 16:30:42

LuoguP2161 [SHOI2009]会场预约的相关文章

[LuoguP2161[ [SHOI2009]会场预约 (splay)

题面 传送门:https://www.luogu.org/problemnew/show/P2161 Solution splay 的确有线段树/树状数组的做法,但我做的时候脑残没想到 我们可以考虑写一个类似NOIP2017D2T3列队那道题那样的带分裂的平衡树 考虑用splay维护每一条线段的左端点和右端点 因为我们题目的意思保证了在平衡树里的线段不相交,所以我们可以考虑以下的性质 每一条线段作为一个点放入平衡树中,维护其L,R,并记录它是空白线段还是有预约的线段 我们要查询一段区间,设这个区

[SHOI2009] 会场预约 - Treap

Description PP大厦有一间空的礼堂,可以为企业或者单位提供会议场地.这些会议中的大多数都需要连续几天的时间(个别的可能只需要一天),不过场地只有一个,所以不同的会议的时间申请不能够冲突.也就是说,前一个会议的结束日期必须在后一个会议的开始日期之前.所以,如果要接受一个新的场地预约申请,就必须拒绝掉与这个申请相冲突的预约. 一般来说,如果PP大厦方面事先已经接受了一个会场预约,例如从10日到15日,就不会在接受与之相冲突的预约,例如从12日到17日.不过,有时出于经济利益,PP大厦方面

[SHOI2009]会场预约

\(ORZ\)据说这个题有四种写法-我孔乙己表示只会一种.(暂时的) \(\color{red}{Description}\) PP大厦有一间空的礼堂,可以为企业或者单位提供会议场地.这些会议中的大多数都需要连续几天的时间(个别的可能只需要一天),不过场地只有一个,所以不同的会议的时间申请不能够冲突.也就是说,前一个会议的结束日期必须在后一个会议的开始日期之前.所以,如果要接受一个新的场地预约申请,就必须拒绝掉与这个申请相冲突的预约. 一般来说,如果PP大厦方面事先已经接受了一个会场预约,例如从

P2161 [SHOI2009]会场预约 - 线段树染色

是真的染色,把不同预约看做不同颜色,现在问题就是一个区间内不同颜色的数量,这个分块线段树都能做吧(不考虑复杂度用莫队也行) 注意,线段树的最大边界必须是定值,不能随输入改变(一开始懒得离线动态更新右端点然后节点的编号就串了) 注意数组大小,因为same和tag数组都是针对线段树节点设置的,所以其数组大小也要开4倍 #include <algorithm> #include <iostream> #include <cstring> #include <cstdio

P2161 [SHOI2009]会场预约

这一题是用来练习stl的,,, stl的set固然很方便, 但是在c++98里erase好像是没有返回值的, 不能像c++11一样 it = S.erase(it); 所以c++98里删掉以后最好重新找以防RE. 具体在这道题中, 就是每一次lower_bound以后看看能不能删前面的或者后面的. c++98真是反人类啊...什么时候noip能够用c++11呢. #include <set> #include <cstdio> #include <cstring> #i

SHOI 2009 会场预约 平衡树 STL练习

题目描述 PP大厦有一间空的礼堂,可以为企业或者单位提供会议场地.这些会议中的大多数都需要连续几天的时间(个别的可能只需要一天),不过场地只有一个,所以不同的会议的时间申请不能够冲突.也就是说,前一个会议的结束日期必须在后一个会议的开始日期之前.所以,如果要接受一个新的场地预约申请,就必须拒绝掉与这个申请相冲突的预约. 一般来说,如果PP大厦方面事先已经接受了一个会场预约,例如从10日到15日,就不会在接受与之相冲突的预约,例如从12日到17日.不过,有时出于经济利益,PP大厦方面有时会为了接受

会场预约

题目描述 PP大厦有一间空的礼堂,可以为企业或者单位提供会议场地.这些会议中的大多数都需要连续几天的时间(个别的可能只需要一天),不过场地只有一个,所以不同的会议的时间申请不能够冲突.也就是说,前一个会议的结束日期必须在后一个会议的开始日期之前.所以,如果要接受一个新的场地预约申请,就必须拒绝掉与这个申请相冲突的预约.一般来说,如果PP大厦方面事先已经接受了一个会场预约,例如从10日到15日,就不会在接受与之相冲突的预约,例如从12日到17日.不过,有时出于经济利益,PP大厦方面有时会为了接受一

[SHOI 2009] 会场预约

[题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=2028 [算法] 直接用std :: set维护即可 时间复杂度 : O(NlogN) [代码] #include<bits/stdc++.h> using namespace std; int n; struct segment { int l , r; friend bool operator < (segment a , segment b) { if (a.r ==

线段树基操

P1816 忠诚 st表竟然写挂了,线段树一遍过(就当练码力) P1198 [JSOI2008]最大数 1.动态在末尾插入一个数 2.输出末尾的L个数的最大值 线段树维护! P3870 [TJOI2009]开关 P2574 XOR的艺术 P2846 [USACO08NOV]光开关Light Switching 三倍经验题!!! 对一段01区间取^1,则若要修改这个o,这段区间的1的个数=区间长度-这段区间的1的个数(t[o].yihuo) 对于一个o,是否修改这个区间,可以对t[o].tag^1