P3968 [TJOI2014] 电源插排
P3968 [TJOI2014] 电源插排
题目描述
小 M 的实验室有很多电源插排。这些插排的编号从
每天早晨,这些插排都是没有被使用的。每当一个学生来到实验室,他就将自己的笔记本电源插到某一个未被使用的插排上。
实验室的同学们都很奇怪,他们完成这个过程是这样的:首先,他们找到还没有被使用的插排的最长区间。
如果有多个区间长度相同,他们就选择最靠右的那个。然后将自己的电源插到该区间的中间。
如果区间长度是偶数,他们同样选择靠右的那个。当一个同学离开实验室时,他会将自己的电源拔出来。
数据保证每一个同学来到实验室时,至少有一个空的插排。
需要计算在区间
输入格式
第一行是两个整数
接下来
如果
为 ,接下来就是两个整数 和 ,表示一个询问。 否则
表示表示编号为 的学生到来或离开。 的奇数次出现表示到来,偶数次出现表示离开。每个学生的编号都是唯一的。
输出格式
对于每一个询问,输出一个整数,表示询问区间内有多少个插排已经被使用。
Solution:
非常有意思的线段树题,我们考虑动态开点维护一颗线段树以维护区间最长连续0序列的长度及其中点坐标还有当前区间下有多少个插排正在使用
首先,对于n=1e9的数据我们显然不能也不需要将每个点都开全,我们只需要知道一些区间上的最长0序列,所以我们只需要在update的时候新开节点就好了
然后我们可以敏锐的察觉到这题的关键就在于push_up:
push_up:
int mid=l+r>>1; t[x].cnt=t[ls].cnt+t[rs].cnt; t[x].lmx=t[ls].lmx+ (t[ls].mx==(mid-l+1) ? t[rs].lmx : 0); t[x].rmx=t[rs].rmx+ (t[rs].mx==(r-mid) ? t[ls].rmx : 0); t[x].mx=0; if(t[rs].mx>t[x].mx) { t[x].mx=t[rs].mx; t[x].mid=t[rs].mid; } if(t[ls].rmx+t[rs].lmx>t[x].mx) { t[x].mx=t[ls].rmx+t[rs].lmx; t[x].mid=mid-t[ls].rmx+1+(t[x].mx/2); } if(t[ls].mx>t[x].mx) { t[x].mx=t[ls].mx; t[x].mid=t[ls].mid; }
看完代码我们不难发现,除了左右区间合并取得最大值时的mid我们比较难思考,剩下的和最长0序列/最大子段和一类题目的写法几乎没有区别
所以我们来重点推一下
if(t[ls].rmx+t[rs].lmx>t[x].mx) { t[x].mx=t[ls].rmx+t[rs].lmx; t[x].mid=mid-t[ls].rmx+1+(t[x].mx/2); }
mid是当前区间的中点,也是t[ls].rmx结束的地方,所以
然后
但是我们有不难发现,我们的t[x].mid取的是
也就是说,我们是在左闭端点上加了区间长度的一半(向下取整)这样我们取到的中点应该是向上取整的
然后这题貌似就做完了,但是还有一个十分坑的点:
“?”判断的优先级在"="号之后,例如:
t[x].lmx=t[ls].lmx+ (t[ls].mx==(mid-l+1) ? t[rs].lmx : 0);
不等价于
t[x].lmx=t[ls].lmx+ t[ls].mx==(mid-l+1) ? t[rs].lmx : 0;
前者会先判断问号语法内的bool并返回相应的值,而后者会直接忽略“+”号之后的内容
至于我为什么知道嘛...
我是永远不会承认我们数据结构选手目光呆滞的
Code:
#include<bits/stdc++.h> const int N=4e5+5; using namespace std; int n,m,cnt,rt; int a[N]; map<int,int> Map; //Segment_Tree struct Tree{ int lmx,rmx,mx,cnt,mid; int ls,rs; }t[N<<4]; #define ls t[x].ls #define rs t[x].rs void add(int &x,int l,int r) { if(x)return ; x=++cnt; t[x].lmx=t[x].rmx=t[x].mx=r-l+1; t[x].mid=l+r+1>>1; } void push_up(int x,int l,int r) { int mid=l+r>>1; t[x].cnt=t[ls].cnt+t[rs].cnt; t[x].lmx=t[ls].lmx+ (t[ls].mx==(mid-l+1) ? t[rs].lmx : 0); t[x].rmx=t[rs].rmx+ (t[rs].mx==(r-mid) ? t[ls].rmx : 0); t[x].mx=0; if(t[rs].mx>t[x].mx) { t[x].mx=t[rs].mx; t[x].mid=t[rs].mid; } if(t[ls].rmx+t[rs].lmx>t[x].mx) { t[x].mx=t[ls].rmx+t[rs].lmx; t[x].mid=mid-t[ls].rmx+1+(t[x].mx/2); } if(t[ls].mx>t[x].mx) { t[x].mx=t[ls].mx; t[x].mid=t[ls].mid; } } void upd(int &x,int l,int r,int pos,int k) { if(!x)add(x,l,r); if(l==r) { t[x].lmx=t[x].rmx=t[x].mx=!k; t[x].cnt=k; return ; } int mid=l+r>>1; add(ls,l,mid);add(rs,mid+1,r); if(pos<=mid)upd(ls,l,mid,pos,k); else upd(rs,mid+1,r,pos,k); push_up(x,l,r); } void query(int x,int l,int r,int L,int R,int &res) { if(!x)return ; if(L<=l&&r<=R) { res+=t[x].cnt; return ; } int mid=l+r>>1; if(L<=mid)query(ls,l,mid,L,R,res); if(mid<R) query(rs,mid+1,r,L,R,res); } void work() { cin>>n>>m; add(rt,1,n); for(int i=1,k,l,r;i<=m;i++) { scanf("%d",&k); if(k) { if(Map[k]) { upd(rt,1,n,Map[k],0); Map[k]=0; } else { Map[k]=t[rt].mid;upd(rt,1,n,Map[k],1); } } else { scanf("%d%d",&l,&r); int ans=0;query(rt,1,n,l,r,ans); printf("%d\n",ans); } } } int main() { //freopen("P3968.in","r",stdin);freopen("P3968.out","w",stdout); work(); return 0; }