题目大意
随着时间的推移这里有几个任务对应着一段区间。每次要将任务安到时间线上时,要把时间线上已有的与该任务对应区间有交集的区间对应的任务删去。求每次删去的区间个数,以及整个时间线上有几个任务。时间线长度m<=100000,任务个数n<=200000
题解
对于一个新加入的区间,我们要从右往左枚举每一个左端点小于等于新区间右端点的任务,直到枚举到了一个任务其右端点小于新任务的左端点。对于每一个要枚举到的任务,我们要尽可能缩小找到它的时间复杂度。于是建立一个权值树状数组,key值时间,值是左端点位于该点的任务个数。其维护的就是左端点个数的前缀和了,它满足单调性。左端点所在的地方就是权值增加的地方,而对于一个新区间,新区间右端点所在的权值前缀和就是离该点左侧的第一个左端点所在位置的权值前缀和。于是我们可以轻松用LowerBound得到区间,就这样不断删除二分删除二分(我们对于每一个左端点整一个数组维护其对应的右端点),直到有一个区间右端点小于新区间左端点为止。极端复杂度$O(n^2 log m)$。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAX_LEN = 100010, TotLen = 100000; int LmatchR[MAX_LEN]; struct BST { private: int C[MAX_LEN]; int N; int Lowbit(int x) { return x & -x; } public: BST(int n):N(n){} int Query(int p) { int ans = 0; while (p > 0) { ans += C[p]; p -= Lowbit(p); } return ans; } void Update(int p, int delta) { while (p <= N) { C[p] += delta; p += Lowbit(p); } } }g(TotLen); int PrefixFind; bool GtPrefixFind(int k) { return g.Query(k) >= PrefixFind; } int LowerBound(int l, int r, bool(*InRange)(int)) { if (!InRange(r)) return -1; while (l < r) { int mid = (l + r) / 2; if (InRange(mid)) r = mid; else l = mid + 1; } return l; } int main() { int qCnt; scanf("%d", &qCnt); while (qCnt--) { char op; int newL, newR, curL, delCnt; scanf(" %c", &op); switch (op) { case 'A': scanf("%d%d", &newL, &newR); delCnt = 0; while (true) { PrefixFind = g.Query(newR); if (PrefixFind == 0) break; curL = LowerBound(1, newR, GtPrefixFind); if (curL == -1) break; if (LmatchR[curL] >= newL) { delCnt++; g.Update(curL, -1); } else break; } g.Update(newL, 1); LmatchR[newL] = newR; printf("%d ", delCnt); break; case 'B': printf("%d ", g.Query(TotLen)); break; } } }