【题目泛做】学军信友队欢乐赛 E (线段树)(凸包)(单调栈)

【题目泛做】学军信友队欢乐赛 E (线段树)(凸包)(单调栈)_第1张图片
【题目泛做】学军信友队欢乐赛 E (线段树)(凸包)(单调栈)_第2张图片

  • 神题

  • 考虑维护上界和下界,下界是个区间求和
    上界是个阶梯状的(前缀 m a x max max 和 后缀 m a x max max
    假设询问全部是 [ 1 , n ] [1,n] [1,n],那么我们预处理一个点在 [ l i , r i ] [l_i,r_i] [li,ri] 的时间内作为前缀最大值
    然后按时间排序,插入当前作为最大值的点,线段树维护这个单调栈的贡献
    现在需要解决区间是 [ l , r ] [l,r] [l,r] 的情况,这种情况我们在线段树上拆分成 l o g log log 个区间,按顺序跟前面的拼接,于是发现要知道在 [ l , r ] [l,r] [l,r] 中作为前缀最大值的点集
    就是要预处理仅考虑 [ l , r ] [l,r] [l,r] 的区间,每个点作为前缀最大值的出现时间区间
    这个是一个动态半平面交问题,我们考虑对线段树每个点维护一个凸包,然后从底向上跳,如果是右儿子就在左儿子里的凸包查询,卡出出现时间的范围

  • 总结(一句话题解):拆成 l o g log log 个查询区间,依次考虑跟前面的拼接(这个需要线段树维护单调栈在线段树上二分),内层线段树维护单调栈的贡献,预先用线段树和凸包预处理出每个点在每个区间作为前缀最大值的时间,每个结点按时间排序动态维护前缀最大值的集合吗,复杂度每一个过程都是 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

#include
#define cs const
#define pb push_back
using namespace std;
typedef long long ll;
typedef long double ld;
template<typename T> T read(){
	T cnt=0, f=1; char ch=0;
	while(!isdigit(ch)){ ch=getchar(); if(ch=='-') f=-1; }
	while(isdigit(ch)) cnt=cnt*10+(ch-'0'), ch=getchar();
	return cnt*f;
}
int gi(){ return read<int>(); }
ll gl(){ return read<ll>(); }
cs int N = 3e5 + 50;
cs ll INF = 2.1e12;
struct L{ 
	ll k, b; 
	ld f(ld x){ return x * k + b; }
}a[N];
struct pnt{ ld x, y; };
struct qry{ int t,l,r; };
qry q[N];
int n, m; ll mx[N], as[N];
struct node{ int opt, c; ll tim; };
#define mid ((l+r)>>1)
vector<node> G[N<<2];
vector<pnt> A[N<<2];
vector<L> B[N<<2];
ld crs(L a, L b){ return 1.0 * (a.b-b.b) / (b.k-a.k); }
void calc(vector<L>&B, vector<pnt>&A, ll &lp, ll &rp, L now){
	int p=lower_bound(B.begin(),B.end(),now,[](L a, L b){ return a.k<b.k; })-B.begin()-1;
	if(now.f(A[p].x)>=A[p].y){
		int res=lower_bound(A.begin(),A.begin()+p+1,now,[](pnt a, L t){ return !(t.f(a.x)>=a.y); })-A.begin();
		lp=crs(B[res],now)-1;
		for(int T=3;T;T--) if(now.f(lp)<B[res].f(lp)) ++lp;
		res=lower_bound(A.begin()+p,A.end(),now,[](pnt a, L t){ return t.f(a.x)>=a.y; })-A.begin();
		rp=crs(B[res],now)+1;
		for(int T=3;T;T--) if(now.f(rp)<B[res].f(rp)) --rp;
	} else lp=1,rp=0;
}
void sub_work(int x, int p, ll L, ll R){
	G[x].pb((node){1,p,L});
	G[x].pb((node){-1,p,R});
	if(x==1) return;
	if(x&1){
		ll lp,rp; calc(B[x^1],A[x^1],lp,rp,a[p]);
		L=max(L,lp); R=min(R,rp);
	} if(L>R) return; sub_work(x>>1,p,L,R);
}
void build(int x, int l, int r){
	if((x&1)^1){
		static vector<L> tmp; tmp.clear(); for(int i=l; i<=r; i++) tmp.pb(a[i]);
		sort(tmp.begin(),tmp.end(),[](cs L &i, cs L &j){
			return i.k==j.k ? i.b>j.b : i.k<j.k;
		});
		tmp.insert(tmp.begin(),(L){(ll)-2e6,-INF});
		tmp.pb((L){0ll,-INF});
		A[x].clear(); B[x].clear(); vector<L> &S=B[x];
		for(auto t : tmp){
			if(S.size()&&S.back().k==t.k) continue;
			while(S.size()>=2&&crs(S[S.size()-2],S[S.size()-1])>=crs(t,S[S.size()-2])) S.pop_back();
			S.pb(t);
		}
		for(int i=1; i<(int)S.size(); i++){
			ld c = crs(S[i-1],S[i]); 
			A[x].pb((pnt){c,S[i].f(c)});
		}
	} G[x].clear();
	if(l==r) { sub_work(x,l,1,1e6); return; }
	build(x<<1,l,mid); build(x<<1|1,mid+1,r);
}
#undef mid
namespace SGT{
	struct data{
		bool hv; int pre; ll Rk, Rb, Sk, Sb;
		friend data operator + (cs data &a, cs data &b){
			return (data){a.hv||b.hv, a.hv?a.pre:a.pre+b.pre, 
			b.hv?b.Rk:a.Rk, b.hv?b.Rb:a.Rb, a.Sk+b.Sk+a.Rk*b.pre, a.Sb+b.Sb+a.Rb*b.pre};
		}
	};
	data v[N<<2];
	#define mid ((l+r)>>1)
	void pushup(int x){ v[x]=v[x<<1]+v[x<<1|1]; }
	void build(int x, int l, int r){
		v[x]=(data){0,r-l+1,0,0,0,0}; if(l==r) return;
		build(x<<1,l,mid); build(x<<1|1,mid+1,r);
	}
	void mdf(int x, int l, int r, int p){
		if(l==r){
			if(v[x].hv) v[x]=(data){0,r-l+1,0,0,0,0};
			else v[x]=(data){1,0,a[l].k,a[l].b,a[l].k,a[l].b};
			return;
		} (p<=mid)?mdf(x<<1,l,mid,p):mdf(x<<1|1,mid+1,r,p); pushup(x);
	}
	int fnd(int x, int l, int r, ll X, ll mx){
		if(l==r) return l; data lv=v[x<<1];
		if(lv.hv&&lv.Rk*X+lv.Rb>mx) return fnd(x<<1,l,mid,X,mx);
		else return fnd(x<<1|1,mid+1,r,X,mx);
	}
	data query(int x, int l, int r, int p){
		if(l>=p) return v[x]; 
		if(p>mid) return query(x<<1|1,mid+1,r,p);
		return query(x<<1,l,mid,p)+query(x<<1|1,mid+1,r,p);
	}
	#undef mid
}
namespace SGT_Main{
	#define mid ((l+r)>>1)
	void ins(int x, int l, int r, int L, int R, int c){
		if(L<=l&&r<=R){ G[x].pb((node){0,c,q[c].t}); return; }
		if(L<=mid) ins(x<<1,l,mid,L,R,c); if(R>mid) ins(x<<1|1,mid+1,r,L,R,c); 
	}
	void work(int x, int l, int r){
		sort(G[x].begin(),G[x].end(),[](cs node &i, cs node &j){
			return i.tim == j.tim ? i.opt > j.opt : i.tim < j.tim;
		}); SGT::build(1,l,r);
		for(auto t : G[x]){
			if(t.opt) SGT::mdf(1,l,r,t.c);
			else{
				ll Rk=SGT::v[1].Rk, Rb=SGT::v[1].Rb, X=q[t.c].t;
				if(Rk*X+Rb<=mx[t.c]) as[t.c]+=mx[t.c]*(r-l+1);
				else{
					int p=SGT::fnd(1,l,r,X,mx[t.c]);
					SGT::data now=SGT::query(1,l,r,p); // suffix 
					as[t.c]+=mx[t.c]*(p-l)+now.Sk*X+now.Sb;
					mx[t.c]=Rk*X+Rb; 
				}
			}
		} if(l==r) return; work(x<<1,l,mid); work(x<<1|1,mid+1,r);
	}
	#undef mid
}
void pre_work(){
	build(1,1,n); // convex 
	for(int i=1; i<=m; i++) 
	SGT_Main::ins(1,1,n,q[i].l,q[i].r,i), mx[i]=-INF;
	SGT_Main::work(1,1,n);
	reverse(a+1,a+n+1);
	for(int i=1; i<=m; i++) 
	q[i]=(qry){q[i].t, n-q[i].r+1, n-q[i].l+1};	
}
int main(){
	n=gi(), m=gi();
	for(int i=1; i<=n; i++) a[i].b=gl();
	for(int i=1; i<=n; i++) a[i].k=-gl();
	for(int i=1; i<=m; i++) q[i].t=gi(), q[i].l=gi(), q[i].r=gi();
	pre_work(); pre_work();
	static ll Sk[N], Sb[N];
	for(int i=1; i<=n; i++) Sk[i]=Sk[i-1]+a[i].k, Sb[i]=Sb[i-1]+a[i].b;
	for(int i=1; i<=m; i++){
		ll t=q[i].t; int l=q[i].l, r=q[i].r;
		as[i]=as[i]-mx[i]*(r-l+1)-((Sk[r]-Sk[l-1])*t+Sb[r]-Sb[l-1]); 
		cout<<as[i]<<'\n';
	} return 0;
}

你可能感兴趣的:(FSY的好题汇总,凸包,线段树)