首页 > 其他分享 >P2161 [SHOI2009]会场预约 题解

P2161 [SHOI2009]会场预约 题解

时间:2023-06-17 22:34:07浏览次数:56  
标签:node cnt const int 题解 P2161 SHOI2009 区间 fhq

蒟蒻提供一种fhq-treap的做法,但是不如其他题解的快(也没有stl快,不开O2 1.8s),但是比较好想,扩展了fhq的模板,也算是为使用fhq提供一个新方法。

首先,fhq-treap是什么,如果有同学不清楚,请点击学习(并非我的blog)

那么,由于fhq树的分裂操作,使得我们可以很方便的取出我们想要的区间。

那么,在这道题里,我们想要的区间是什么呢?

首先,对于B操作,我们只需要维护一个cnt,让他每次减掉删除的数量再加1就好了,这很容易。

但是,对于一次A x操作,我们需要找到所有与x区间重合的区间进行删除,删除好说,那怎么找呢?

我们这里改动一下传统fhq的模板,让存储的“关键码”val变成区间(以下代码中的node型)

struct FHQ_Treap{
   int l,r;
   int dat,siz;
   node val;
}t[N];

然后定义node的比较操作:

  • 区间x==区间y,当且仅当x,y有重叠部分
  • 区间x<区间y,当且仅当x在y左边并且没有重叠,即x的右端点小于y的左端点
  • 区间x<=区间y,当且仅当x==y或x<y

代码如下:

struct node{
	int l,r;
	bool operator==(const node &o)const{
		return (l<=o.r&&o.r<=r)||(l<=o.l&&o.l<=r)||(o.l<=r&&r<=o.r)||(o.l<=l&&l<=o.r);
	}
	bool operator<(const node &o)const{
		return (r<o.l);
	}
	bool operator<=(const node &o)const{
		return (*this)<o||(*this)==o;
	}
};

现在,我们把模板复制过来,改一下变量类型,然后你就会发现,过不了样例!

原因是啥呢?试想如果有两个区间\(x=[1,4]\),\(y=[7,9]\),现在插入区间\(i=[2,8]\)

那么就只会删除其中一个,因为x与y没有重合,换句话说!(x==y),(注意不能写成x!=y)。

那咋办呢?

我们发现,虽然!(x==y),但是x==iy==i,那么我们先插入一次i,那么就可以将x和y“连起来”,让他们一起被删除。

所以我们只要先插入一次i再删除就好啦

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2e6+5;
struct node{
	int l,r;
	bool operator==(const node &o)const{
		return (l<=o.r&&o.r<=r)||(l<=o.l&&o.l<=r)||(o.l<=r&&r<=o.r)||(o.l<=l&&l<=o.r);
	}
	bool operator<(const node &o)const{
		return (r<o.l);
	}
	bool operator<=(const node &o)const{
		return (*this)<o||(*this)==o;
	}
};
class FTreap{
	public:
    struct FHQ_Treap{
    	int l,r;
    	int dat,siz;
    	node val;
    }t[N];
    #define l(p) t[p].l
    #define r(p) t[p].r
    #define dat(p) t[p].dat
    #define val(p) t[p].val
    #define siz(p) t[p].siz
    int tot;
    int New(node val){
    	int np=++tot;
    	val(np)=val;
    	dat(np)=rand();
    	siz(np)=1;
    	return np;
    }
    void Pushup(int p){
    	siz(p)=siz(l(p))+siz(r(p))+1;
    }
    int root;const int INF=INT_MAX;
    void SplitByVal(int p,int &x,int &y,node val)
    {
    	if(!p){
    		x=y=0; 
    		return;
    	}
    	if(val(p)<=val){
    		x=p,SplitByVal(r(p),r(x),y,val);
    	}else{
    		y=p,SplitByVal(l(p),x,l(y),val);
    	}
    	Pushup(p);//分裂后,p的左或右子树已经改变 
    }
    void SplitByVal2(int p,int &x,int &y,node val)
    {
    	if(!p){
    		x=y=0; 
    		return;
    	}
    	if(val(p)<val){
    		x=p,SplitByVal2(r(p),r(x),y,val);
    	}else{
    		y=p,SplitByVal2(l(p),x,l(y),val);
    	}
    	Pushup(p);//分裂后,p的左或右子树已经改变 
    }
    int Merge(int x,int y){//返回合并后的root 
    	if(!x||!y) return x+y;//有一子树不存在,直接合并
    	if(dat(x)<dat(y)){//按照大根堆性质合并 (确定上下关系)
    		l(y)=Merge(x,l(y));//注意我们不能把参数传反了 
    		Pushup(y);
    		return y; 
    	}else{
    		r(x)=Merge(r(x),y);
    		Pushup(x);
    		return x;
    	}
    }
    int Insert(node val){
    	int t1=0,t2=0,t3=0;
    	SplitByVal(root,t1,t2,val);
		int tt1=Merge(t1,New(val));
    	root=Merge(tt1,t2);
    	t1=0,t2=0,t3=0;
    	SplitByVal(root,t1,t2,val);
    	SplitByVal2(t1,t1,t3,val);
    	int ret=siz(t3)-1;//注意这里不包括我们之前插进去的那个,所以要-1
    	root=Merge(Merge(t1,New(val)),t2);
		return ret;
    }
}tr;
int n;
int main(){
	cin>>n;
	int cnt=0;
	while(n--){
		char op;int l,r;
		cin>>op;
		if(op=='A'){
			cin>>l>>r;
			int res=tr.Insert({l,r});
			cnt-=res;cnt++;
			cout<<res<<endl;
		}else cout<<cnt<<endl;
	}
}

标签:node,cnt,const,int,题解,P2161,SHOI2009,区间,fhq
From: https://www.cnblogs.com/haozexu/p/17488384.html

相关文章

  • react经典面试题解析--持续更新--day01
    一、类组件和函数组件的区别(面试常考)简单理解(所有同学都要掌握)1、类组件有生命周期,函数组件没有2、类组件需要继承Class,函数组件不需要3、类组件可以获取实例化的this,并且基于this做各种操作,函数组件不行4、类组件内部可以定义并维护state,函数组件都称为无状态了,那肯定......
  • AtCoder ABC056D 题解
    题目直达0x00思路从大到小枚举每个元素,同时加入\(sum\)进行累计,当\(k\lesum\)时,便会返现之前的元素可以构成“好的组”(因为他们都大于\(p_i\)),即有用的,所以要清空。0x01举个例子36143我们对输入排序后,会得到\(p\)为。431这时,我们的\(i=1\),即\(p_i=......
  • AtCoder ABC228D 题解
    [ABC299D]FindbyQuery题解0x00题目分析题目传送门经过分析,我们得到的几个关键信息:\(n\le2\times10^5\)最多可以问法官\(20\)个问题。s[1]=0,s[n]=1分析\(n\)的范围,发现\(\log_n=17.6096\),刚好比\(20\)小一点点。这时便考虑二分的做法。看到s[1]=0,......
  • AtCoder ABC047D 题解
    题意理解&分析:大概的题意应该是十分清晰的,就是一个人要从\(1\)到\(n\)的城市中买苹果。另一个人要其中调整价格。这里的调整也不需要太多,就\(1\)就可以了。但是,如果有多组购买方案可以得到相同的利润,就还需要将其他相同的价格一并调整。这道题的关键就在于求出有几组购买......
  • CF1732D1 题解
    CF1732D1Balance题解题目解释输入一个\(op\)和\(x\),\(op\)有\(2\)种情况。\(op\)为+,则将\(x\)加入到集合中。\(op\)为?,则找到一个最小的\(k\),使\(k\timesx\)不在合集中。题目非常简单明了。具体实现这时,看到这句话:\(1\lex\le10^{18}\),所以不可......
  • 题解 CF1830C【Hyperregular Bracket Strings】
    卡特兰数一个长为\(2n\)的序列,填入括号,有多少种合法方案?\(C_n=\sum_iC_iC_{n-i-1}\),其中\(C_0=C_1=1\)。它的封闭形式是\(C_n=\binom{2n}{n}-\binom{2n}{n-1}\)。problem给定一个长度\(n\)和\(k\)个子区间\(\{[l1​,r1​],[l2​,r2​],…,[lk​,rk​]\}\)。问有多......
  • 【题解】CF754D Fedor and coupons(优先队列)
    【题解】CF754DFedorandcoupons题目链接CF754DFedorandcouponsCF1029CMaximalIntersection后者是前者的加强版。思路分析最开始,先考虑不删区间\((k=0)\)的情况:也就是给你一大堆区间,让你找他们的交集。这个还是比较好想的,我们刚开始让第二个区间与第一个区间相交......
  • 【CF1841C 题解】
    首先,我们把\(s\)翻转。考虑dp,\(f_{i,j,k}\)表示到了第\(i\)个字符,操作了\(j\)个字符,最大的字符为\(k\)的最大值。转移时枚举\(i-1\)的最大字符\(\ell(0\le\ell<5)\)。不操作第\(i\)个字符;\(f_{i,j,k}=\max\{f_{i-1,j,\ell}+w\}\)。操作第\(i\)......
  • POJ2117 Electricity 题解 tarjan点双连通分量 割点
    题目链接:http://poj.org/problem?id=2117题目大意:给定一个由\(n\)个点\(m\)解题思路:tarjan,判断\(u\)的子节点有几个\(v\)满足\(low[v]\gedfn[u]\)就是答案,但是同时如果\(u\)不是这个dfs树的根节点,个数还要加\(1\)。示例程序1(C++11,POJ不支持):#include<bits/stdc++.h>......
  • P2860 [USACO06JAN]Redundant Paths G 题解 tarjan边双连通分量
    题目链接:https://www.luogu.com.cn/problem/P2860题目大意:给定一个无向连通图,求至少加几条边,能使其变成一个边双连通图。解题思路:边双连通分量缩点后计算度数为\(1\)的节点个数,假设有\(cnt\)个,则答案为\((cnt+1)/2\)。示例程序:#include<bits/stdc++.h>usingnamespacestd;......