1. 程式人生 > 其它 >GYM350340K.King Kog's Reception(巧妙的線段樹)

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