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