[CF453E][平衡树][主席树]Little Pony and Lord Tirek

CF453E

先不考虑清空,类似于一道叫siano的线段树题,我们把所有点按照加满的时间排序,然后某一个时刻满能量的就一定是一个后缀,然后就可以用线段树维护了
对于这道题,我们再用一个set维护一下删除时间相同的一段,每次查询就删除之前的并插入当前的,查询sum可以用主席树,分别维护前缀和后缀(满和未满),就是类似siano的方法

Code:

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=2e5+5;
int n;
ll v[N],m[N],s[N],t[N];
namespace President_tree{
	int rt[N],cnt=0;
	struct seg{int l,r;ll sum1,sum2;}tr[N*40];
	#define ls(k) tr[k].l
	#define rs(k) tr[k].r
	void ins(int &rt1,int rt2,int l,int r,int pos,int id){
		rt1=++cnt;
		tr[rt1]=tr[rt2];
		tr[rt1].sum1+=m[id];
		tr[rt1].sum2+=v[id];
		if(l==r) return;
		int mid=l+r>>1;
		if(pos<=mid) ins(ls(rt1),ls(rt2),l,mid,pos,id);
		else ins(rs(rt1),rs(rt2),mid+1,r,pos,id);
	}
	ll query1(int rt1,int rt2,int l,int r,int ql,int qr){
		if(ql<=l && r<=qr) return tr[rt1].sum1-tr[rt2].sum1;
		int mid=l+r>>1;ll res=0;
		if(ql<=mid) res+=query1(ls(rt1),ls(rt2),l,mid,ql,qr);
		if(qr>mid) res+=query1(rs(rt1),rs(rt2),mid+1,r,ql,qr);
		return res;
	}
	ll query2(int rt1,int rt2,int l,int r,int ql,int qr){
		if(ql<=l && r<=qr) return tr[rt1].sum2-tr[rt2].sum2;
		int mid=l+r>>1;ll res=0;
		if(ql<=mid) res+=query2(ls(rt1),ls(rt2),l,mid,ql,qr);
		if(qr>mid) res+=query2(rs(rt1),rs(rt2),mid+1,r,ql,qr);
		return res;
	}
}
using namespace President_tree;
struct Q{
	int l,r,pos;ll tim;
	friend inline bool operator < (Q a,Q b){return a.l<b.l;} 
}a[N];
set<Q>S;
vector<Q>vec;
int tot;
int main(){
	int n=read();
	for(int i=1;i<=n;i++){
		s[i]=read(),m[i]=read(),v[i]=read();
		if(v[i]) t[i]=(m[i]-1)/v[i]+1;
		else t[i]=1e10;
	}
	sort(t+1,t+n+1);
	tot=unique(t+1,t+n+1)-t-1;
	for(int i=1;i<=n;i++){
		int pos;
		if(v[i]) pos=lower_bound(t+1,t+tot+1,(m[i]-1)/v[i]+1)-t;
		else pos=tot;
		ins(rt[i],rt[i-1],1,tot,pos,i);
	}
	for(int i=1;i<=n;i++){
		Q tmp;tmp.l=tmp.r=i;tmp.pos=s[i];tmp.tim=0;
		S.insert(tmp);
	}
	int q=read();
	while(q--){
		int Time=read(),l=read(),r=read();
		Q tmp;tmp.pos=0;tmp.l=l;tmp.r=r;tmp.tim=Time;
		set<Q>::iterator it=S.upper_bound(tmp);--it;
		int cur=0;
		while(1){
			if((*it).l>r || it==S.end()) break;
			vec.pb((*it));
			++cur;++it;
		}
		for(int i=0;i<cur;i++) S.erase(S.find(vec[i]));
		if(vec[0].l<l){
			Q tmp1;
			tmp=vec[0];tmp1=tmp;tmp1.r=l-1;
			S.insert(tmp1);vec[0].l=l;
		}
		if(vec[cur-1].r>r){
			Q tmp1;
			tmp=vec[cur-1];tmp1=tmp;tmp1.l=r+1;
			S.insert(tmp1);vec[cur-1].r=r;
		}
		tmp.pos=0;tmp.l=l;tmp.r=r;tmp.tim=Time;
		S.insert(tmp);
		ll ans=0;
		for(int i=0;i<cur;i++){
			if(vec[i].tim){
				int pos=upper_bound(t+1,t+tot+1,Time-vec[i].tim)-t-1;
				if(pos) ans+=query1(rt[vec[i].r],rt[vec[i].l-1],1,tot,1,pos);
				ans+=query2(rt[vec[i].r],rt[vec[i].l-1],1,tot,pos+1,tot)*(Time-vec[i].tim);
			}
			else ans+=min(m[vec[i].l],s[vec[i].l]+Time*(v[vec[i].l]));
		}
		vec.clear();
		cout<<ans<<"\n";
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值