[CTSC2015]日程管理
Solution
鉴于其于我的教育性意义,所以决定还是不放在寒假橄榄计划😢里面了。
我们首先考虑没有修改操作时候怎么做。发现可以套用拟阵模型。首先遗传性是肯定的,一个合法的方案集合其子集必然合法。然后我们需要考虑证明其拓展性,对于两个方案集合 \(A,B\),设 \(|A|<|B|\),需要判断是否存在 \(x\in B \backslash A\) 且 \(A\cap \{x\}\) 也是合法集合。注意到最优情况下 \(A,B\) 都占用的都是时间的一个前缀,设 \(B_{|A|+1,...,|B|}\) 这一段为 \(S\),如果 \(S\not\subseteq A\),那么显然是合法的。否则,我们可以从 \(A\) 中属于 \(S\) 的元素中任取一个元素提到 \(S\) 中对应的位置,这样原位置就空出来了,然后就可以递归下去(指可以把空位即以后都删除当做 \(A\) 继续递归)。可以发现最后一定会合法。
所以我们的贪心策略就很显然了,我们按权值从大到小加入,然后每次判断能否加入即可。至于判断的话,判断条件即是 \(\forall i\in [1,T],\sum_{(t,p)} [t\le i] \le i\),这个可以用线段树维护每个 \(i\) 的 \(i-\sum_{(t,p)} [t\le i]\)最小值,修改的话即是一个后缀 \(\pm 1\)。
然后考虑实时维护,可以发现其实就跟最大生成树一个套路了。加入的时候先加入,如果合法则不管了。否则,我们找到线段树上 \(<0\) 的最小的位置 \(q\),然后把当前集合 \(t\in [1,q]\) 中最小的 \(p\) 删除即可。删除的时候也差不多,如果本来就不在最优集合里,则可以不管,否则先删除,然后找到线段树上 \(=0\) 的最大的位置 \(q\),则可以加入 \(t\in [q+1,T]\) 的最大的 \(p\)。可以看出删除一个,再补的话最多只会补一个,否则如果能加 \(2\) 个的话就会违反拓展性。
上面的步骤可以用两个线段树加可删堆/set维护,复杂度 \(\mathcal O(n\log n)\)。
Code
#include <bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f3fll
#define Int register int
#define int long long
#define MAXN 300005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}
int T,n;
#define pii pair<int,int>
#define se second
#define fi first
struct Que{
priority_queue <pii> q1,q2;
void ins (pii it){q1.push (it);}
void del (pii it){q2.push (it);}
void pushit (){while (!q1.empty() && !q2.empty() && q1.top() == q2.top()) q1.pop (),q2.pop ();}
pii Top (){pushit ();return q1.empty() ? make_pair(-inf,0ll) : q1.top();}
bool Empty(){pushit ();return q1.empty();}
};
struct Seg{
int tag[MAXN << 2],miv[MAXN << 2];
void pushup (int x){miv[x] = min (miv[x << 1],miv[x << 1 | 1]);}
void pushadd (int x,int v){miv[x] += v,tag[x] += v;}
void pushdown (int x){pushadd (x << 1,tag[x]),pushadd (x << 1 | 1,tag[x]),tag[x] = 0;}
void modify (int x,int l,int r,int ql,int qr,int v){
if (l >= ql && r <= qr) return pushadd (x,v);
int mid = l + r >> 1;pushdown (x);
if (ql <= mid) modify (x << 1,l,mid,ql,qr,v);
if (qr > mid) modify (x << 1 | 1,mid + 1,r,ql,qr,v);
pushup (x);
}
int queryl (int x,int l,int r){
if (l == r) return l;
int mid = l + r >> 1;pushdown (x);
if (miv[x << 1] == miv[x]) return queryl (x << 1,l,mid);
else return queryl (x << 1 | 1,mid + 1,r);
}
int queryr (int x,int l,int r){
if (l == r) return l;
int mid = l + r >> 1;pushdown (x);
if (miv[x << 1 | 1] == miv[x]) return queryr (x << 1 | 1,mid + 1,r);
else return queryr (x << 1,l,mid);
}
void build (int x,int l,int r){
miv[x] = l;
if (l == r) return ;
int mid = l + r >> 1;
build (x << 1,l,mid),build (x << 1 | 1,mid + 1,r);
}
void ins (int t){modify (1,1,T,t,T,-1);}
void del (int t){modify (1,1,T,t,T,1);}
}tr;
struct Segment{
map <pii,int> mp;
Que sT[MAXN];int mip[MAXN << 2],sum;
void pushup (int x){mip[x] = sT[mip[x << 1]].Top() > sT[mip[x << 1 | 1]].Top() ? mip[x << 1] : mip[x << 1 | 1];}
void ins (int x,int l,int r,int t,int p){
if (l == r) return sT[t].ins ({p,t}),sum += p,void ();
int mid = l + r >> 1;
if (t <= mid) ins (x << 1,l,mid,t,p);
else ins (x << 1 | 1,mid + 1,r,t,p);
pushup (x);
}
void del (int x,int l,int r,int t,int p){
if (l == r) return sT[t].del ({p,t}),sum -= p,void ();
int mid = l + r >> 1;
if (t <= mid) del (x << 1,l,mid,t,p);
else del (x << 1 | 1,mid + 1,r,t,p);
pushup (x);
}
pii query (int x,int l,int r,int ql,int qr){
if (l >= ql && r <= qr) return sT[mip[x]].Top();
int mid = l + r >> 1;pii res = {-inf,0};
if (ql <= mid) chkmax (res,query (x << 1,l,mid,ql,qr));
if (qr > mid) chkmax (res,query (x << 1 | 1,mid + 1,r,ql,qr));
return res;
}
void Ins (int t,int p){ins (1,1,T,t,p),mp[{t,p}] ++;}
void Del (int t,int p){del (1,1,T,t,p),mp[{t,p}] --;}
bool checkit (int t,int p){return mp[{t,p}];}
void build (int x,int l,int r){
if (l == r) return mip[x] = l,void ();
int mid = l + r >> 1;
build (x << 1,l,mid),build (x << 1 | 1,mid + 1,r),pushup (x);
}
}T1,T2;
void addit (int t,int p){
tr.ins (t),T1.Ins (t,-p);
if (tr.miv[1] >= 0) return ;
int pos = tr.queryl (1,1,T);pii it = T1.query (1,1,T,1,pos);
T1.Del (it.se,it.fi),T2.Ins (it.se,-it.fi),tr.del (it.se);
}
void delit (int t,int p){
if (T2.checkit (t,p)) return T2.Del (t,p);
tr.del (t),T1.Del (t,-p);
int pos = tr.miv[1] > 0 ? 0 : tr.queryr (1,1,T);pii it = T2.query (1,1,T,pos + 1,T);
if (it.fi == -inf) return ;
T2.Del (it.se,it.fi),T1.Ins (it.se,-it.fi),tr.ins (it.se);
}
signed main(){
read (T,n),tr.build (1,1,T),T1.build (1,1,T),T2.build (1,1,T);
while (n --> 0){
char str[10] = {};scanf ("%s",str + 1);int t,p;read (t,p);
if (str[1] == 'A') addit (t,p);
else delit (t,p);
write (-T1.sum),putchar ('\n');
}
return 0;
}