GYM350340K.King Kog's Reception(巧妙的线段树)

维护一个队列,每个人有抵达时间和操作时间。

要求维护以下三种操作:

1)t时刻来一个人,它操作需要d秒。

2)t时刻要来的那个人不来了。

3)如果t时刻去排队,需要等多久才能轮到你。

做法:

巧妙的线段树,以时间为下标建树,每个节点维护两个信息:

1)这个区间内所有来的人的操作时间之和c。

2)完成这个区间内所有人的最早截至时间mx。

那么有转移方程:

1)c[i]=c[i<<1]+c[i<<1|1]

2)mx[i]=max(mx[i<<1]+c[i<<1|1],mx[i<<1|1])

正常单点修改+区间查询。

有点线段树上树形DP的感觉。很牛,我确实想不到,一种船新的技巧。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int q;
long long mx[maxn<<2],c[maxn<<2];
void build (int i,int l,int r) {
	if (l==r) {
		mx[i]=l;
		return;
	}
	int mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
	c[i]=c[i<<1]+c[i<<1|1];
	mx[i]=max(mx[i<<1]+c[i<<1|1],mx[i<<1|1]);
} 
void up (int i,int l,int r,int p,int v) {
	if (l==r) {
		c[i]+=v;
		mx[i]+=v;
		return;
	}
	int mid=(l+r)>>1;
	if (p<=mid) up(i<<1,l,mid,p,v);
	if (p>mid) up(i<<1|1,mid+1,r,p,v);
	c[i]=c[i<<1]+c[i<<1|1];
	mx[i]=max(mx[i<<1]+c[i<<1|1],mx[i<<1|1]);
}
pair<long long,long long> query (int i,int l,int r,int L,int R) {
	if (l>=L&&r<=R) return {c[i],mx[i]};
	pair<long long,long long> ans={0,0};
	int mid=(l+r)>>1;
	if (L<=mid) {
		pair<long long,long long> tt=query(i<<1,l,mid,L,R);
		ans.first=ans.first+tt.first;
		ans.second=max(ans.second+tt.first,tt.second);
	}
	if (R>mid) {
		pair<long long,long long> tt=query(i<<1|1,mid+1,r,L,R);
		ans.first=ans.first+tt.first;
		ans.second=max(ans.second+tt.first,tt.second);
	}
	return ans;
}
pair<int,int> qq[maxn];
int main () {
	cin>>q;
	int mm=1e6;
	build(1,1,mm);
	for (int i=1;i<=q;i++) {
		//getchar();
		char op;
		int t,d;
		cin>>op;
		if (op=='?') {
			int x;
			cin>>x;
			printf("%lld\n",query(1,1,mm,1,x).second-x);
		}
		else if (op=='+') {
			int t,d;
			cin>>t>>d;
			qq[i]={t,d};
			up(1,1,mm,t,d);
		}
		else {
			int p;
			cin>>p;
			up(1,1,mm,qq[p].first,-qq[p].second);
		}
	}
}
posted @ 2021-10-22 22:08  zlc0405  阅读(25)  评论(0编辑  收藏  举报