P3968 [TJOI2014] 电源插排
P3968 [TJOI2014] 电源插排
题目描述
小 M 的实验室有很多电源插排。这些插排的编号从 \(1\) 到 \(n\),由左向右排成一排。
每天早晨,这些插排都是没有被使用的。每当一个学生来到实验室,他就将自己的笔记本电源插到某一个未被使用的插排上。
实验室的同学们都很奇怪,他们完成这个过程是这样的:首先,他们找到还没有被使用的插排的最长区间。
如果有多个区间长度相同,他们就选择最靠右的那个。然后将自己的电源插到该区间的中间。
如果区间长度是偶数,他们同样选择靠右的那个。当一个同学离开实验室时,他会将自己的电源拔出来。
数据保证每一个同学来到实验室时,至少有一个空的插排。
需要计算在区间 \([l,r]\) 已经有多少个插排被使用了。
输入格式
第一行是两个整数 \(n\) 和 \(q\),表示插排数量和询问数量。
接下来 \(q\) 行,每一行以一个整数 \(k\) 开头。
如果 \(k\) 为 \(0\),接下来就是两个整数 \(l\) 和 \(r\),表示一个询问。
否则 \(k\) 表示表示编号为 \(k\) 的学生到来或离开。\(k\) 的奇数次出现表示到来,偶数次出现表示离开。每个学生的编号都是唯一的。
输出格式
对于每一个询问,输出一个整数,表示询问区间内有多少个插排已经被使用。
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结束的地方,所以
\(mid-t[ls].rmx+1\)表示的就是t[ls].rmx开始的地方
然后\(t[x].mx/2\)显然就是区间长度(向下取整)但是这题让我们维护的中点貌似是向上取整的啊qwq
但是我们有不难发现,我们的t[x].mid取的是
\(l+ (len/2)\) 而非 \(l+(len/2)-1\)
也就是说,我们是在左闭端点上加了区间长度的一半(向下取整)这样我们取到的中点应该是向上取整的
然后这题貌似就做完了,但是还有一个十分坑的点:
“?”判断的优先级在"="号之后,例如:
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;
}

浙公网安备 33010602011771号