1. 程式人生 > >bzoj2049 線段樹 + 可撤銷並查集

bzoj2049 線段樹 + 可撤銷並查集

printf bzoj2049 end problem 題意 print geo typedef 過程

https://www.lydsy.com/JudgeOnline/problem.php?id=2049

線段樹真神奇

題意:給出一波操作,拆邊加邊以及詢問兩點是否聯通。

聽說常規方法是在線LCT,留坑。

如果說這個刪邊的操作是刪除上一條邊,那這自然是可撤銷並查集的模板題,直接在線維護就可以了。

但是問題在於刪除邊的順序是不可能固定的,要知道並查集是不可以隨意撤銷的。

萬萬沒想到還有更加高妙的手法。

首先可以證明一條邊的存在一定是一段或者多段連續的區間。

建立一條時間節點長度的線段樹,結點維護一個邊集合,每個位置表示的是當前這個時間下存在了哪幾條邊。

將上述的邊區間全部加入,和常規的線段樹不一樣,這個不需要lazy標記也不需要Pushdown到下屬區間,為了節省時間和空間,對於1 - N區間的邊來說,我們僅僅把1號結點加上這條邊。

然後用dfs的方法,進入結點時加上這些邊,離開的時候刪除這些邊,在線段樹的葉子節點上,並查集維護的就是當前時間的狀態,離線的query直接詢問即可。

時間復雜度,加邊的整個過程mlogm,詢問的過程節點數mlogm * 並查集find操作logm = mlog2 m

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include 
<string> #include <cstdio> #include <cstdlib> #include <cstring> #include <sstream> #include <iostream> #include <algorithm> #include <functional> using namespace std; #define For(i, x, y) for(int i=x;i<=y;i++) #define _For(i, x, y) for(int i=x;i>=y;i--) #define
Mem(f, x) memset(f,x,sizeof(f)) #define Sca(x) scanf("%d", &x) #define Sca2(x,y) scanf("%d%d",&x,&y) #define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z) #define Scl(x) scanf("%lld",&x); #define Pri(x) printf("%d\n", x) #define Prl(x) printf("%lld\n",x); #define CLR(u) for(int i=0;i<=N;i++)u[i].clear(); #define LL long long #define ULL unsigned long long #define mp make_pair #define PII pair<int,int> #define PIL pair<int,long long> #define PLL pair<long long,long long> #define pb push_back #define fi first #define se second typedef vector<int> VI; int read(){int x = 0,f = 1;char c = getchar();while (c<0 || c>9){if (c == -) f = -1;c = getchar();} while (c >= 0&&c <= 9){x = x * 10 + c - 0;c = getchar();}return x*f;} const double eps = 1e-9; const int maxn = 1e5 + 10; const int maxm = 2e6 + 10; const int INF = 0x3f3f3f3f; const int mod = 1e9 + 7; int N,M,K; struct Query{ int t,u,v; Query(){} Query(int t,int u,int v):t(t),u(u),v(v){} }q[maxm]; struct Line{ int op,u,v; Line(){} Line(int op,int u,int v):op(op),u(u),v(v){} }line[maxm]; map<PII,int>Q; int Stack[maxm],top; int size[maxn],fa[maxn]; void init(){ for(int i = 0; i <= N ; i ++){ fa[i] = -1; size[i] = 0; } top = 0; } //segment_tree struct Tree{ int l,r; int head; }tree[maxm << 2]; struct Edge{ PII data; int next; }edge[maxm << 2]; int tot,cnt,cnt2; void add(int u,PII v){ edge[tot].next = tree[u].head; edge[tot].data = v; tree[u].head = tot++; } void Build(int t,int l,int r){ tree[t].l = l; tree[t].r = r; tree[t].head = -1; if(l == r) return; int m = (l + r) >> 1; Build(t << 1,l,m); Build(t << 1 | 1,m + 1,r); } void update(int t,int l,int r,PII v){ if(l <= tree[t].l && tree[t].r <= r){ add(t,v); return; } int m = (tree[t].l + tree[t].r) >> 1; if(r <= m) update(t << 1,l,r,v); else if(l > m) update(t << 1 | 1,l,r,v); else{ update(t << 1,l,m,v); update(t << 1 | 1,m + 1,r,v); } } int find(int x){ while(fa[x] != -1) x = fa[x]; return x; } void Union(int x,int y){ x = find(x); y = find(y); if(x == y) return; if(size[x] > size[y]) swap(x,y); Stack[top++] = x; fa[x] = y; size[y] += size[x] + 1; } void rewind(int t){ while(top > t){ int x = Stack[--top]; size[fa[x]] -= size[x] + 1; fa[x] = -1; } } void dfs(int t){ int now = top; for(int i = tree[t].head; ~i; i = edge[i].next){ PII v = edge[i].data; Union(v.fi,v.se); } if(tree[t].l == tree[t].r){ while(tot <= cnt2 && q[tot].t == tree[t].l){ if(find(q[tot].u) == find(q[tot].v)){ puts("Yes"); }else{ puts("No"); } tot++; } rewind(now); return; } dfs(t << 1); dfs(t << 1 | 1); rewind(now); } int main(){ Sca2(N,M); init(); cnt = 0,cnt2 = 0; for(int i = 1; i <= M ; i ++){ char op[10]; int u,v; scanf("%s%d%d",op,&u,&v); if(u > v) swap(u,v); if(op[0] == Q) q[++cnt2] = Query(cnt,u,v); else if(op[0] == C) line[++cnt] = Line(0,u,v); else line[++cnt] = Line(1,u,v); } tot = 0; Build(1,0,cnt); for(int i = 1; i <= cnt; i ++){ int &x = Q[mp(line[i].u,line[i].v)]; if(line[i].op == 0) x = i; else{ update(1,x,i - 1,mp(line[i].u,line[i].v)); x = 0; } } for(map<PII,int>::iterator it = Q.begin(); it != Q.end(); it++){ pair<PII,int> u = *it; if(u.se) update(1,u.se,cnt,u.fi); } tot = 1; dfs(1); return 0; }

bzoj2049 線段樹 + 可撤銷並查集