题目大意
给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排序分为两种:(0,l,r)表示将区间[l,r]的数字升序排序;(1,l,r)表示将区间[l,r]的数字降序排序。最后询问第q位置上的数字。n<=30000。
题解
关键词:反演。
我们假设最后q位置上的值为val。此时我们对整个序列进行排序...我们发现除了val外,其它点之间的顺序并不重要,只有其他点与val的相对大小才有意义。所以我们将原序列中位置上的值小于val的的值设为0,大于等于的设为1,整个序列上每个点的值表示的就是序列上的原值与val的大小关系。这样对01值排序用覆盖式的线段树来进行排序过程最方便了(具体看代码中的Sort)。
此时q位置上的值如果是0,则说明当前的val比答案大;若此时q位置上的值是1,则说明当前的val小于或等于答案。也就是说,val越大,最后q位上的值越有可能是0,val越小,q位上的值越有可能是1。因此我们可以用UpperBound二分得出答案。
#include <cstdio> #include <cstring> #include <algorithm> #include <cassert> using namespace std; const int MAX_N = 30010, MAX_NODE = MAX_N * 4, MAX_OP = 30010; int OrgData[MAX_N]; int N, TotOp, P; struct RangeTree { private: struct Node { int Sum, Cover; }_nodes[MAX_NODE]; int N; void PushDown(int cur, int l, int r) { if (_nodes[cur].Cover >= 0) { _nodes[cur * 2].Cover = _nodes[cur].Cover; _nodes[cur * 2 + 1].Cover = _nodes[cur].Cover; int mid = (l + r) / 2; _nodes[cur * 2].Sum = _nodes[cur].Cover * (mid - l + 1); _nodes[cur * 2 + 1].Sum = _nodes[cur].Cover * (r - mid); _nodes[cur].Cover = -1; } } void PullUp(int cur) { _nodes[cur].Sum = _nodes[cur * 2].Sum + _nodes[cur * 2 + 1].Sum; } void Update(int cur, int al, int ar, int sl, int sr, int cover) { assert(al <= sr && ar >= sl && sl <= sr); if (al <= sl && sr <= ar) { _nodes[cur].Cover = cover; _nodes[cur].Sum = cover * (sr - sl + 1); return; } PushDown(cur, sl, sr); int mid = (sl + sr) / 2; if (al <= mid) Update(cur * 2, al, ar, sl, mid, cover); if (ar > mid) Update(cur * 2 + 1, al, ar, mid + 1, sr, cover); PullUp(cur); } int Query(int cur, int al, int ar, int sl, int sr) { assert(al <= sr && ar >= sl && sl <= sr); if (al <= sl && sr <= ar) return _nodes[cur].Sum; PushDown(cur, sl, sr); int mid = (sl + sr) / 2, ans = 0; if (al <= mid) ans += Query(cur * 2, al, ar, sl, mid); if (ar > mid) ans += Query(cur * 2 + 1, al, ar, mid + 1, sr); PullUp(cur); return ans; } void InitBuild(int cur, int sl, int sr, int *a) { if (sl == sr) { _nodes[cur].Sum = a[sl]; _nodes[cur].Cover = -1; return; } int mid = (sl + sr) / 2; InitBuild(cur * 2, sl, mid, a); InitBuild(cur * 2 + 1, mid + 1, sr, a); _nodes[cur].Cover = -1; PullUp(cur); } public: void Init(int n, int *a) { N = n; InitBuild(1, 1, N, a); } void Update(int l, int r, int cover) { if (l > r) return; Update(1, l, r, 1, N, cover); } int Query(int l, int r) { return Query(1, l, r, 1, N); } }g; struct Oper//operation { int L, R; bool IsUp; Oper(){} Oper(int l, int r, int isUp):L(l),R(r),IsUp(isUp){} }_ops[MAX_OP]; void Sort(Oper op) { int sum1 = g.Query(op.L, op.R); if (op.IsUp) { g.Update(op.R - sum1 + 1, op.R, 1); g.Update(op.L, op.R - sum1, 0); } else { g.Update(op.L, op.L + sum1 - 1, 1); g.Update(op.L + sum1, op.R, 0); } } bool AnsNotLesser(int val) { static int a[MAX_N]; for (int i = 1; i <= N; i++) a[i] = (OrgData[i] >= val); g.Init(N, a); for (int i = 1; i <= TotOp; i++) Sort(_ops[i]); return g.Query(P, P) == 1; } int UpperBound(int l, int r, bool(*InRange)(int)) { while (l < r) { int mid = (l + r + 1) / 2; if (InRange(mid)) l = mid; else r = mid - 1; } return l; } int main() { scanf("%d%d", &N, &TotOp); for (int i = 1; i <= N; i++) scanf("%d", OrgData + i); for (int i = 1; i <= TotOp; i++) { int l, r, isDown; scanf("%d%d%d", &isDown, &l, &r); _ops[i] = Oper(l, r, !isDown); } scanf("%d", &P); printf("%d ", UpperBound(1, N, AnsNotLesser)); return 0; }