1. 程式人生 > >CF 319E Ping-Pong 區間的並查集合並

CF 319E Ping-Pong 區間的並查集合並

題目大意

定義區間(a,b)能走到區間(c,d)的條件是c<a<dc<b<d
現在有兩種操作:
Ord=1新加入一個區間(a,b)(後加入的區間保證比新加入的區間長)
Ord=2:詢問從第a個區間能否走到第b個區間。
現在有M個操作,對於第二個操作輸出YESNO

M105
a,b109

解題思路

首先我們先考慮兩個區間連邊的情況,如果不是包含關係,那麼這兩個區間連的就是雙向邊,如果是包含關係,那麼就是小的區間向大的區間連一條單向邊。

我們先討論雙向邊的情況,因為是雙向邊,所以我們能採用並查集維護聯通塊的思想。如果有兩個能互相到達的區間,那麼我們就把它們合併成一個區間,具體的實現可以用線段樹維護。每次加入一個區間可以線上段樹中詢問這個區間的左右端點是否在別的區間上。對於線段樹的一個節點維護覆蓋到這裡的區間,當一個端點訪問到線段樹的一個節點時,就把那些區間合併到當前區間,並把那個節點的的標記賦值為當前區間。其實就是實現了一個區間的並查集的功能。

而對於最後的詢問只需看一下兩個區間是否在同一個並查集。當然如果是覆蓋的情況(單向邊)也要考慮進去。

程式

具體實現可以看程式碼。

//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <map>

using namespace std;

const int MAXN = 2e5 + 5;

struct Query {
    int Ord, x, y;
} Q[MAXN];

vector
<int>
Tr[MAXN * 4]; map<int,int> Hash; vector<int> P; int N, L[MAXN], R[MAXN], Pre[MAXN], Ask[MAXN]; int Get(int Now) { if (Pre[Now] == Now) return Now; Pre[Now] = Get(Pre[Now]); return Pre[Now]; } void Modify(int Now, int l, int r, int Side, int Ord) { if (!Tr[Now].empty()) { for
(int i = 0; i < Tr[Now].size(); i ++) { int Fa = Get(Tr[Now][i]); L[Ord] = min(L[Ord], L[Fa]); R[Ord] = max(R[Ord], R[Fa]); Pre[Fa] = Ord; } Tr[Now].clear(); Tr[Now].push_back(Ord); } if (l == r) return; int Mid = (l + r) >> 1; if (Side <= Mid) Modify(Now * 2, l, Mid, Side, Ord); else Modify(Now * 2 + 1, Mid + 1, r, Side, Ord); } void Push(int Now, int l, int r, int lx, int rx, int Ord) { if (lx > rx) return; if (r < lx || l > rx) return; if (l >= lx && r <= rx) { Tr[Now].push_back(Ord); return; } int Mid = (l + r) >> 1; Push(Now * 2, l, Mid, lx, rx, Ord), Push(Now * 2 + 1, Mid + 1, r, lx, rx, Ord); } bool In(int Side, int l, int r) { return Side < r && Side > l; } int main() { scanf("%d", &N); for (int i = 1; i <= N; i ++) { scanf("%d%d%d", &Q[i].Ord, &Q[i].x, &Q[i].y); if (Q[i].Ord == 1) P.push_back(Q[i].x), P.push_back(Q[i].y); } sort(P.begin(), P.end()); unique(P.begin(), P.end()); int Num = 0, Cnt = 0; int pp = 0; for (int i = 0; i < P.size(); i ++) Hash[P[i]] = ++ Num; for (int i = 1; i <= N; i ++) { if (Q[i].Ord == 1) { int x = Hash[Q[i].x], y = Hash[Q[i].y]; L[++ Cnt] = x, R[Cnt] = y; Pre[Cnt] = Cnt, Ask[Cnt] = i; Modify(1, 1, Num, x, Cnt); Modify(1, 1, Num, y, Cnt); Push(1, 1, Num, L[Cnt] + 1, R[Cnt] - 1, Cnt); } else { pp ++; int x = Q[i].x, y = Q[i].y; int l = Hash[Q[Ask[x]].x], r = Hash[Q[Ask[x]].y]; int fa = Get(y); if (Get(x) == Get(y) || In(l, L[fa], R[fa]) || In(r, L[fa], R[fa])) printf("YES\n"); else printf("NO\n"); } } }