首页 > 其他分享 >【XSY4829】鸽子的彩灯(线段树)

【XSY4829】鸽子的彩灯(线段树)

时间:2022-11-15 08:22:21浏览次数:57  
标签:ch int 线段 XSY4829 pair second 彩灯 区间 ql

大强说很套路,所以记一下。

首先当前的电流就是剩下还未经过的点亮的灯的 \(c_i\) 之和。

考虑一个暴力的做法:初始将所有询问的流量设为 \(0\),从后往前扫过每一个彩灯 \(i\),并把 \(p_i\in [l_j,r_j]\) 的询问 \(j\) 的流量都加上 \(c_i\),若某个询问的流量大于 \(s_i\),那么就把该询问的答案设为 \(i\) 并把该询问扔掉。

考虑证明两个区间有包含关系时,答案出现顺序也有对应的关系:发现对于相互包含的区间,大的区间肯定会比小的区间答案靠后。

那么假设当前所有未扔掉的区间中,有一个区间它被另一个区间包含,我们就先不考虑该区间。这样需要考虑的区间都是互不包含的,于是每次要修改流量的询问是排序后的一段区间的询问,那么就能用数据结构维护了。

每次在数据结构上找到流量最大的询问,如果其流量大于 \(s_i\) 就把它扔掉,此时会加入一些更小的被它包含的区间。用数据结构维护每个 \(R\) 对应的还未被考虑过的 \(L\) 最小的区间,然后每次找 \(R\) 在一段合法范围内的 \(L\) 最小的那个区间即可。

找到一个区间并准备将它设为考虑时,由于以前的 \(c_i\) 的贡献我们是没有给它考虑的,所以我们又需要一个数据结构来直接查询以前的 \(c_i\) 的贡献。

总时间复杂度 \(O(n\log n)\)。

#include<bits/stdc++.h>

#define N 500010

using namespace std;

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

int qL[N],qR[N];
int n,m,s[N],c[N],p[N];
int idx,id[N],rk[N];
int ans[N];

vector<pair<int,int>> q[N];

namespace Seg1
{
	pair<int,int> minl[N<<2];
	pair<int,int> operator + (pair<int,int> a,pair<int,int> b)
	{
		if(a.first==b.first) return a.second>b.second?a:b;
		return a.first<b.first?a:b;
	}
	void up(int k){minl[k]=minl[k<<1]+minl[k<<1|1];}
	void build(int k,int l,int r)
	{
		if(l==r)
		{
			minl[k]=make_pair(q[l].empty()?INT_MAX:q[l].back().first,r);
			return;
		}
		int mid=(l+r)>>1;
		build(k<<1,l,mid);
		build(k<<1|1,mid+1,r);
		up(k);
	}
	pair<int,int> query(int k,int l,int r,int ql,int qr)
	{
		if(ql<=l&&r<=qr) return minl[k];
		int mid=(l+r)>>1;
		if(qr<=mid) return query(k<<1,l,mid,ql,qr);
		if(ql>mid) return query(k<<1|1,mid+1,r,ql,qr);
		return query(k<<1,l,mid,ql,qr)+query(k<<1|1,mid+1,r,ql,qr);
	}
	void update(int k,int l,int r,int x)
	{
		if(l==r)
		{
			assert(!q[l].empty());
			q[l].pop_back();
			minl[k]=make_pair(q[l].empty()?INT_MAX:q[l].back().first,r);
			return;
		}
		int mid=(l+r)>>1;
		if(x<=mid) update(k<<1,l,mid,x);
		else update(k<<1|1,mid+1,r,x);
		up(k);
	}
}

set<pair<int,int>> sl,sr;
namespace Seg2
{
	int lazy[N<<2];
	pair<int,int> maxn[N<<2];
	void up(int k){maxn[k]=max(maxn[k<<1],maxn[k<<1|1]);}
	void downn(int k,int v){maxn[k].first+=v,lazy[k]+=v;}
	void down(int k)
	{
		if(lazy[k])
		{
			downn(k<<1,lazy[k]);
			downn(k<<1|1,lazy[k]);
			lazy[k]=0;
		}
	}
	void build(int k,int l,int r)
	{
		if(l==r)
		{
			maxn[k]=make_pair(-INT_MAX,l);
			return;
		}
		int mid=(l+r)>>1;
		build(k<<1,l,mid);
		build(k<<1|1,mid+1,r);
		up(k);
	}
	void update(int k,int l,int r,int ql,int qr,int v)
	{
		if(ql<=l&&r<=qr){downn(k,v);return;}
		down(k);
		int mid=(l+r)>>1;
		if(ql<=mid) update(k<<1,l,mid,ql,qr,v);
		if(qr>mid) update(k<<1|1,mid+1,r,ql,qr,v);
		up(k);
	}
	void update(int k,int l,int r,int x,int y)
	{
		if(l==r)
		{
			maxn[k]=make_pair(y,l);
			return;
		}
		down(k);
		int mid=(l+r)>>1;
		if(x<=mid) update(k<<1,l,mid,x,y);
		else update(k<<1|1,mid+1,r,x,y);
		up(k);
	}
}

namespace Seg3
{
	#define lowbit(x) (x&-x)
	int c[N];
	inline void add(int x,int y){for(;x<=n;x+=lowbit(x))c[x]+=y;}
	inline int query(int x){int ans=0;for(;x;x-=lowbit(x))ans+=c[x];return ans;}
	inline int query(int l,int r){return query(r)-query(l-1);}
	#undef lowbit
}

void rebuild(int nowl,int nowr,int nxtl,int lstr)
{
	while(lstr<nowr)
	{
		auto seg=Seg1::query(1,1,n,lstr+1,nowr);
		assert(seg.first>=nowl);
		if(seg.first>=nxtl) return;
		int l=seg.first,r=seg.second,qid=q[r].back().second;
		Seg1::update(1,1,n,r);
		Seg2::update(1,1,m,rk[qid],Seg3::query(l,r));
		sl.insert(make_pair(l,rk[qid]));
		sr.insert(make_pair(r,rk[qid]));
		nowl=l,lstr=r;
	}
}

int main()
{
	n=read(),m=read();
	for(int i=1;i<=n;i++) s[i]=read();
	for(int i=1;i<=n;i++) c[i]=read();
	for(int i=1;i<=n;i++) p[read()]=i;
	for(int i=1;i<=m;i++)
	{
		int l=qL[i]=read(),r=qR[i]=read();
		q[r].emplace_back(l,i);
	}
	for(int r=1;r<=n;r++)
	{
		for(auto pr:q[r]) id[rk[pr.second]=++idx]=pr.second;
		sort(q[r].begin(),q[r].end());
		reverse(q[r].begin(),q[r].end());
	}
	Seg1::build(1,1,n);
	Seg2::build(1,1,m);
	rebuild(1,n,n+1,0);
	for(int i=n;i>=1;i--)
	{
		auto ql=sr.lower_bound(make_pair(p[i],0));
		auto qr=sl.lower_bound(make_pair(p[i]+1,0));
		if(ql!=sr.end()&&qr!=sl.begin())
		{
			qr--;
			if((*ql).second<=(*qr).second)
				Seg2::update(1,1,m,(*ql).second,(*qr).second,c[i]);
		}
		Seg3::add(p[i],c[i]);
		while(1)
		{
			if(Seg2::maxn[1].first<=s[i]) break;
			int rid=Seg2::maxn[1].second,qid=id[rid];
			ans[qid]=i;
			Seg2::update(1,1,m,rid,-INT_MAX);
			sl.erase(make_pair(qL[qid],rid));
			sr.erase(make_pair(qR[qid],rid));
			auto nxt=sl.lower_bound(make_pair(qL[qid],0));
			auto lst=sr.lower_bound(make_pair(qR[qid],0));
			rebuild(qL[qid],qR[qid],nxt!=sl.end()?(*nxt).first:n+1,lst!=sr.begin()?(*(--lst)).first:0);
		}
	}
	for(int i=1;i<=m;i++)
		printf("%d\n",ans[i]);
	return 0;
}

标签:ch,int,线段,XSY4829,pair,second,彩灯,区间,ql
From: https://www.cnblogs.com/ez-lcw/p/16891197.html

相关文章

  • 可持久化线段树 学习笔记
    引入嗯嗯因为我打了一次测试所以学了这个可持久化线段树(怎么说其实这东西我很久之前学过,只是有点忘了深刻认识到了写博客的重要性啦!(>ω・*)ノ这东西其实很简单,也加强了......
  • 线段树维护区间历史版本和
    好久没写博客了,主要是这玩意网上有点难搜到,就干脆糊了一个另外区间加操作的题见这个博客给定长为\(n\)的01序列,\(m\)个询问,第\(i\)次认为产生一个新版本\(i\);要......
  • 20221111_T1B_线段树优化建图/并查集
    题意给定一个字符串,其中只有a和b,现在一个字符能够跳到与之中间a的个数范围在\([l,r]\)的东西。题解赛时得分:100/100对于一个东西,显然如果将能相互到达连边,那么......
  • 可持久化线段树
    可持久化线段树可持久化权值线段树·又叫主席树·本质就是多棵线段树·可持久化表示可以维护历史任一版本的数据·例题\(\quad\)·Q1:给定\(n\)个整数构成的......
  • 2022-11-08 行情启动时的拐点,上下上形成了奔走中枢或者线段走势
    案例一:先看今天纳斯达克的一小段上涨1分钟的一段下上下走势引出的5分钟一笔上涨  案例2:EUR/USD 一开始的启动1.4h下跌终于出现最后一跌2.30分钟出现横盘整......
  • 线段树分裂
    #include<bits/stdc++.h>usingnamespacestd;constintmaxn=2e5+5;typedeflonglongll;structNode{intl,r;llval;}sgt[maxn*40];//?40......
  • P3834 【模板】可持久化线段树 2
    先用结构体实现了下,发现写错了(只有20分),后面直接用的数组了  #include<bits/stdc++.h>usingnamespacestd;constintN=2e5+7;inttl[N<<6],tr[N<<6],sum......
  • 线段树(Segment Tree)是一个基于分治的数据结构。
    线段树(SegmentTree)是一个基于分治的数据结构。线段树杂谈 概念:线段树(SegmentTree)是一个基于分治的数据结构。通常处理区间,序列中的查询,更改问题。大体上有单修,单......
  • CF1045G AI Robots (动态开点线段树 + 离散化)(关于其空间复杂度的分析)
    题目大意:火星上有\(N\)个机器人排成一行,第\(i\)个机器人的位置为\(x_i\),视野维\(r_i\),智商为\(q_i\)。我们认为第\(i\)个机器人可以看到的位置是\([x_i-r_i,......
  • 可持久化线段树
    数组一般开maxn<<5,但有的时候也会不够,不知道怎么判断得到的建议是“贴着内存开”。最套路的应用就是各种形式的区间k小:K小数保存一下模板code#include<bits/stdc+......