【线段树+堆】CC_SEGMENTQ Segment Queries

【题目】
CC
一条长度为 n n n的数轴,每个整点初始未被标记,要求支持两种操作:

  • [ l , r ] [l,r] [l,r]存在未被标记的整点,加入一条线段 [ l , r ] [l,r] [l,r]
  • 标记一个整点,输出有多少条线段在标记整点后,线段范围内的点全部被标记。一个点不会被重复标记。
    n , Q ≤ 1 0 6 n,Q\leq 10^6 n,Q106,要求强制在线。

【解题思路】
考虑标记一个点 x x x会对哪些线段产生影响。我们分别找到这个点左边和右边第一个未被标记的点 l , r l,r l,r,那么左端点在 [ l + 1 , x ] [l+1,x] [l+1,x],右端点在 [ x , r − 1 ] [x,r-1] [x,r1]的所有线段都会有贡献。

由于每个线段只会被贡献一次,不妨将每条线段按左端点插入线段树中,叶子节点维护一个堆,记下以当前位置为左端点的右端点最左的线段是什么。然后线段树上每个节点维护区间有多少个左端点,以及这些左端点对应的最小的右端点。

这样加入的时候直接加入对应堆并更新信息,标记根据线段树上维护的信息来得到答案。

复杂度 O ( ( n + Q ) log ⁡ n ) O((n+Q)\log n) O((n+Q)logn)

【参考代码】

#include<bits/stdc++.h>
using namespace std;

const int N=1e6+10,inf=0x3f3f3f3f;
int n,Q,ans,lef[N],rig[N];
priority_queue<int,vector<int>,greater<int> >q[N];

namespace IO
{
	int read()
	{
		int ret=0;char c=getchar();
		while(!isdigit(c)) c=getchar();
		while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
		return ret;
	}
	void write(int x){if(x>9)write(x/10);putchar(x%10^48);}
	void writeln(int x){write(x);putchar('\n');}
}
using namespace IO;

struct Segment
{
#define ls (x<<1)
#define rs (x<<1|1)
	int mi[N<<2];
	void build(int x,int l,int r)
	{
		mi[x]=inf;
		if(l==r) return;
		int mid=(l+r)>>1;
		build(ls,l,mid);build(rs,mid+1,r);
	}
	void insert(int x,int l,int r,int L,int R)
	{
		if(l==r)
		{
			q[l].push(R);mi[x]=min(mi[x],R);
			return;
		}
		int mid=(l+r)>>1;
		if(L<=mid) insert(ls,l,mid,L,R);
		else insert(rs,mid+1,r,L,R);
		mi[x]=min(mi[ls],mi[rs]);
	}
	void query(int x,int l,int r,int L,int R,int lim)
	{
		if(l==r)
		{
			while(!q[l].empty() && q[l].top()<=lim) q[l].pop(),++ans; 
			mi[x]=q[l].empty()?inf:q[l].top();
			return;
		}
		int mid=(l+r)>>1;
		if(L<=mid && mi[ls]<=lim) query(ls,l,mid,L,R,lim);
		if(R>mid && mi[rs]<=lim) query(rs,mid+1,r,L,R,lim);
		mi[x]=min(mi[ls],mi[rs]);
	}
#undef ls
#undef rs
}T;

int findl(int x){return lef[x]==x?x:lef[x]=findl(lef[x]);}
int findr(int x){return rig[x]==x?x:rig[x]=findr(rig[x]);}

int main()
{
#ifdef Durant_Lee
	freopen("CC_SEGMENTQ.in","r",stdin);
	freopen("CC_SEGMENTQ.out","w",stdout);
#endif
	n=read();Q=read();
	for(int i=0;i<=n+1;++i) lef[i]=rig[i]=i;
	T.build(1,1,n);
	while(Q--)
	{
		int op=read(),x=read(),y;
		if(!op)
		{
			y=read();
			if(findr(x)<=y) T.insert(1,1,n,x,y);
		}
		else
		{
			int l=findl(x-1)+1,r=findr(x+1)-1;ans=0;lef[x]=l-1;rig[x]=r+1;
			T.query(1,1,n,l,x,r);writeln(ans);fflush(stdout);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值