P3968 [TJOI2014] 电源插排

P3968 [TJOI2014] 电源插排

题目描述

小 M 的实验室有很多电源插排。这些插排的编号从 1n,由左向右排成一排。

每天早晨,这些插排都是没有被使用的。每当一个学生来到实验室,他就将自己的笔记本电源插到某一个未被使用的插排上。

实验室的同学们都很奇怪,他们完成这个过程是这样的:首先,他们找到还没有被使用的插排的最长区间。

如果有多个区间长度相同,他们就选择最靠右的那个。然后将自己的电源插到该区间的中间。

如果区间长度是偶数,他们同样选择靠右的那个。当一个同学离开实验室时,他会将自己的电源拔出来。

数据保证每一个同学来到实验室时,至少有一个空的插排。

需要计算在区间 [l,r] 已经有多少个插排被使用了。

输入格式

第一行是两个整数 nq,表示插排数量和询问数量。

接下来 q 行,每一行以一个整数 k 开头。

如果 k0,接下来就是两个整数 lr,表示一个询问。

否则 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结束的地方,所以
midt[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并返回相应的值,而后者会直接忽略“+”号之后的内容

至于我为什么知道嘛...

十七岁花季少年竟然为了它破防7小时

我是永远不会承认我们数据结构选手目光呆滞的

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;
}
posted @   liuboom  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示