LOJ #2585. 「APIO2018」新家
#2585. 「APIO2018」新家
分析:
线段树+二分。
首先看怎样数颜色,正常的时候,离线扫一遍右端点,每次只记录最右边的点,然后查询左端点,这里不太行。这里只需要统计是否全出现过,pre[i]为这个颜色的上一个位置,那么这也就说明了pre[i]+1这段区间没出现过,所以要求[r+1,n]这段区间的最小的pre都要大于等于l。于是这就是线段树区间查询最小值了。
注意的是,每个点的pre有多个,每个叶子节点包含一个set,把所有的值插入进去,取最小值。用两个堆维护也可以,(取最小值,删除一个值)。
然后就可以二分一个长度,然后查询[x-len,x+len]这段区间是否都有所有的颜色就行了。复杂度$O(nlog^2n)$
一个log的做法,二分的过程放到了线段树上。
代码:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 #include<cctype> 7 #include<set> 8 #include<queue> 9 #include<vector> 10 #include<map> 11 using namespace std; 12 typedef long long LL; 13 14 inline int read() { 15 int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; 16 for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; 17 } 18 19 const int N = 300005; 20 const int INF = 1e9; 21 22 struct Node{ 23 int opt, x, col, t; 24 Node() {} 25 Node(int a,int b,int c,int d) { opt = a, x = b, col = c, t = d; } 26 bool operator < (const Node &A) const { 27 return t == A.t ? opt > A.opt : t < A.t; // 如果时间相同,先加入,在询问,再删除 28 } 29 }A[N * 3]; 30 struct Heap{ 31 priority_queue<int, vector<int>, greater<int> > q1, q2; 32 int size() { return q1.size() - q2.size(); } 33 void add(int x) { q1.push(x); } 34 void del(int x) { q2.push(x); } 35 int top() { 36 while (!q2.empty() && q1.top() == q2.top()) q1.pop(), q2.pop(); 37 return q1.top(); 38 } 39 // multiset<int> s; 40 // int size() { return s.size(); } 41 // void add(int x) { s.insert(x); } 42 // void del(int x) { s.erase(s.find(x)); } 43 // int top() { return *s.begin(); } 44 }H[N]; 45 multiset<int> S[N]; 46 int ans[N], Id[N * 30], ls[N * 30], rs[N * 30], Mn[N * 30]; 47 int n, k, m, Index_Heap, Index_Tree, Root, Now_Col; 48 49 void update(int l,int r,int &rt,int p,int u,int v) { 50 if (!rt) rt = ++Index_Tree; 51 if (l == r) { 52 if (!Id[rt]) Id[rt] = ++Index_Heap; 53 Heap &now = H[Id[rt]]; 54 if (u) now.add(u); 55 if (v) now.del(v); 56 Mn[rt] = now.size() ? now.top() : INF; 57 return ; 58 } 59 int mid = (l + r) >> 1; 60 if (p <= mid) update(l, mid, ls[rt], p, u, v); 61 else update(mid + 1, r, rs[rt], p, u, v); 62 Mn[rt] = min(Mn[ls[rt]], Mn[rs[rt]]); 63 } 64 void add(const Node &now) { 65 multiset<int> &s = S[now.col]; 66 multiset<int> :: iterator r = s.upper_bound(now.x), l = r; l--; 67 update(0, INF, Root, *r, now.x, *l); 68 update(0, INF, Root, now.x, *l, 0); 69 if (s.size() == 2) Now_Col ++; 70 s.insert(now.x); 71 } 72 void del(const Node &now) { 73 multiset<int> &s = S[now.col]; 74 s.erase(s.find(now.x)); 75 if (s.size() == 2) Now_Col --; 76 multiset<int> :: iterator r = s.upper_bound(now.x), l = r; l--; 77 update(0, INF, Root, *r, *l, now.x); 78 update(0, INF, Root, now.x, 0, *l); 79 } 80 int Ask(int p) { 81 if (Now_Col != k) return -1; 82 int l = 0, r = INF, now = Root, ans = INF; 83 while (l != r) { // 模拟在线段树上走的过程 84 int mid = (l + r) >> 1, tmp = min(ans, Mn[rs[now]]); 85 if (p <= mid && tmp + mid >= p + p) ans = tmp, r = mid, now = ls[now]; 86 else l = mid + 1, now = rs[now]; 87 } 88 return l - p; 89 } 90 int main() { 91 n = read(), k = read(), m = read(); 92 Mn[0] = INF; 93 for (int i = 1; i <= k; ++i) // 初始往INF处加入k个-INF 94 S[i].insert(-INF), S[i].insert(INF), update(0, INF, Root, INF, -INF, 0); 95 int tot = 0; 96 for (int i = 1; i <= n; ++i) { 97 int x = read(), t = read(), a = read(), b = read(); 98 A[++tot] = Node(1, x, t, a); A[++tot] = Node(-1, x, t, b); 99 } 100 for (int i = 1; i <= m; ++i) { 101 int x = read(), t = read(); 102 A[++tot] = Node(0, x, i, t); 103 } 104 sort(A + 1, A + tot + 1); 105 for (int i = 1; i <= tot; ++i) { 106 if (A[i].opt == 1) add(A[i]); 107 else if (A[i].opt == -1) del(A[i]); 108 else ans[A[i].col] = Ask(A[i].x); 109 } 110 for (int i = 1; i <= m; ++i) printf("%d\n",ans[i]); 111 return 0; 112 }