【set+线段树】CF833E Caramel Clouds

【题目】
Codeforces
n n n个线段覆盖区间 [ l i , r i ] [l_i,r_i] [li,ri],移除它花费 c i c_i ci,你有 C C C的花费上限。 Q Q Q次询问一个值 x x x,在至多移除两条线段且花费不超过上限的情况下,求一个最小的 y y y满足 [ 0 , y ] [0,y] [0,y]没有被覆盖的区间长度大于等于 x x x
n , Q ≤ 3 × 1 0 5 , l i , r i , c i ≤ 1 0 9 n,Q\leq 3\times 10^5,l_i,r_i,c_i\leq 10^9 n,Q3×105,li,ri,ci109

【解题思路】
这个题就很大力啊。

首先我们不妨将以这些线段的端点为界将整个区间分成至多 O ( n ) O(n) O(n)段,然后考虑答案在每个区间中会对哪些询问产生贡献。

  • 被三条线段所覆盖的区间显然不会对答案产生贡献。
  • 没有被线段覆盖的区间会和前面的最长可贡献区间一起对答案贡献。
  • 被两条线段所覆盖的区间,那么我们一定会移除这两条线段(如果可行的话),于是贡献就是这两天线段单独覆盖的区间加上这个区间再加上所有没有被线段覆盖的区间。
  • 被一条线段覆盖的区间,那么在移除这条线段的基础上还要移除前面另一条。此时假设两条线段没有交,那么我们一定是选择前面单独覆盖区间最长的一条线段(当然你不需要特判是否没有交,因为如果选择了有交的一定会在后一种考虑到)。若两条线段有交,那我们还需要考虑同时被这两条线段覆盖的区间,这个区间一定在前面已经遍历过,那么当我们在前面遍历的时候,我们给它打上一个贡献标记在后面这条线段上,这样就可以统计了。(这些标记的总数也只有 O ( n ) O(n) O(n)

上面的贡献计算可以使用 set + \text{set}+ set+线段树维护。(前者用于维护当前覆盖了什么线段)
复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

【参考代码】

#include<bits/stdc++.h>
#define fi first
#define se second
#define mkp make_pair
using namespace std;

typedef pair<int,int> pii;
const int N=6e5+10,inf=2e9+10;
int n,Q,C,cnt;
int ans[N],cro[N],len[N];
pii q[N];
set<int>st;
map<int,int>mp[N];

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');}

struct cloud
{
	int l,r,c;
	cloud(int _l=0,int _r=0,int _c=0):l(_l),r(_r),c(_c){}
	bool operator < (const cloud&rhs)const{return c<rhs.c;}
}cl[N];
struct node 
{
	int t,op,id;
	node(int _t=0,int _o=0,int _i=0):t(_t),op(_o),id(_i){}
	bool operator < (const node&rhs)const{return t<rhs.t;}
}a[N];

void gmax(int &x,int y){x=max(x,y);}
int calc(int x,int y){if(x>y)swap(x,y);return len[x]+len[y]+mp[x][y];}

struct Segment
{
#define ls (x<<1)
#define rs (x<<1|1)
	int t[N<<2];
	void pushup(int x){t[x]=max(t[ls],t[rs]);}
	void update(int x,int l,int r,int p,int w)
	{
		if(l==r){t[x]=w;return;}
		int mid=(l+r)>>1;
		if(p<=mid) update(ls,l,mid,p,w);
		else update(rs,mid+1,r,p,w);
		pushup(x);
	}
	int getpos(int x,int l,int r)
	{
		if(l==r) return l;
		int mid=(l+r)>>1;
		return t[ls]>t[rs]?getpos(ls,l,mid):getpos(rs,mid+1,r);
	}
	int cmax(int x,int y){return len[x]>len[y]?x:y;}
	int query(int x,int l,int r,int L,int R)
	{
		if(L<=l && r<=R) return getpos(x,l,r);
		int mid=(l+r)>>1,res=0;
		if(L<=mid) res=cmax(res,query(ls,l,mid,L,R));
		if(R>mid) res=cmax(res,query(rs,mid+1,r,L,R));
		return res;
	}
#undef ls
#undef rs
}tr;

int main()
{
#ifdef Durant_Lee
	freopen("CF833E.in","r",stdin);
	freopen("CF833E.out","w",stdout);
#endif
	n=read();C=read();
	for(int i=1;i<=n;++i) cl[i].l=read(),cl[i].r=read(),cl[i].c=read();
	sort(cl+1,cl+n+1);cl[n+1]=(cloud){0,0,inf};
	for(int i=1;i<=n;++i) a[++cnt]=node(cl[i].l,1,i),a[++cnt]=node(cl[i].r,-1,i);
	sort(a+1,a+cnt+1);a[++cnt]=node(inf,1,n+1);
	Q=read();for(int i=1;i<=Q;++i) q[i].fi=read(),q[i].se=i;
	sort(q+1,q+Q+1);

	for(int i=1,las=0,pos=1,sum=0,del=0;i<=cnt;++i)
	{
		int L=a[i].t-las;las=a[i].t;
		if(st.empty()) sum+=L;
		else if(st.size()==1)
		{
			int x=*st.begin();
			len[x]+=L;cro[x]+=L;tr.update(1,1,n,x,len[x]);
			if(cl[x].c<=C)
			{
				int l=1,r=upper_bound(cl+1,cl+n+1,cloud(0,0,C-cl[x].c))-cl-1,val=len[x];
				if(l<=x && x<=r)
				{
					if(l<=x-1) gmax(val,calc(x,tr.query(1,1,n,l,x-1)));
					if(x+1<=r) gmax(val,calc(x,tr.query(1,1,n,x+1,r)));
				}
				else if(l<=r) gmax(val,calc(x,tr.query(1,1,n,l,r)));
				gmax(cro[x],val);gmax(del,cro[x]);
			}
		}
		else if(st.size()==2)
		{
			int x=*st.begin(),y=*st.rbegin();mp[x][y]+=L;
			if(cl[x].c+cl[y].c<=C)
			{
				int tmp=calc(x,y);
				gmax(cro[x],tmp);gmax(cro[y],tmp);
				gmax(del,max(cro[x],cro[y]));
			}
		}
		for(;pos<=Q && del+sum>=q[pos].fi;++pos) ans[q[pos].se]=las-(del+sum-q[pos].fi);
		if(a[i].op==1) st.insert(a[i].id);
		else st.erase(a[i].id);
	}
	for(int i=1;i<=Q;++i) writeln(ans[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值