1. 程式人生 > >luogu3224 永無鄉(動態開點,權值線段樹合並)

luogu3224 永無鄉(動態開點,權值線段樹合並)

線段 har query oot fin sin tdi %d 初始

luogu3224 永無鄉(動態開點,權值線段樹合並)

永無鄉包含 n 座島,編號從 1 到 n ,每座島都有自己的獨一無二的重要度,按照重要度可以將這 n 座島排名,名次用 1 到 n 來表示。某些島之間由巨大的橋連接,通過橋可以從一個島到達另一個島。如果從島 a 出發經過若幹座(含 0 座)橋可以 到達島 b ,則稱島 a 和島 b 是連通的。現在有兩種操作:B x y 表示在島 x 與島 y 之間修建一座新橋。Q x k 表示詢問當前與島 x 連通的所有島中第 k 重要的是哪座島,即所有與島 x 連通的島中重要度排名第 k 小的島是哪座,請你輸出那個島的編號。n≤100000,m≤n,q≤300000。

首先,對於每個島建一個權值線段樹,然後根據初始邊將它們合並。對於在同一個聯通塊裏的點,通過並查集選出其中一個點x,root[x]代表這個聯通塊的線段樹的根。對於查詢,第k重要的島可以通過在權值線段樹裏的二分來確定。合並時,規定將x合並到y。由於初始時,權值線段樹的空間是nlogn,並且不會增加點,所以空間復雜度是nlogn。查詢的時間復雜度是logn,雖然兩顆線段樹合並的時間復雜度是nlogn,但是每個線段樹中的結點最多被歸並一次,並且歸並的次數=被歸並的次數,所以線段樹合並的總時間復雜度也是nlogn。

#include <cctype>
#include <cstdio>
using namespace std;

const int maxn=1e5+5, maxseg=maxn*20;
//v:表示一個島的重要性 id:重要性反推是哪個結點 fa:用來找到與線段樹對應的結點
int n, m, q, v[maxn], id[maxn], fa[maxn];
//root:將島的編號映射在線段樹根位置上 lc,rc:線段樹結點的左右孩子
int cnt, root[maxn], lc[maxseg], rc[maxseg], seg[maxseg];

void get(int
&x){ int flag=1; char c; for (c=getchar(); !isdigit(c); c=getchar()) if (c=='-') flag=-1; for (x=c-48; c=getchar(), isdigit(c); ) x=(x<<3)+(x<<1)+c-48; x*=flag; } int find(int x){ return x==fa[x]?x:fa[x]=find(fa[x]); } //給權值線段樹增加一個值 void add(int &x, int
l, int r, int pos){ if (!x) x=cnt++; if (l==r){ ++seg[x]; return; } int mid=(l+r)>>1; if (pos>mid) add(rc[x], mid+1, r, pos); else add(lc[x], l, mid, pos); seg[x]=seg[lc[x]]+seg[rc[x]]; } //將x歸並到y上,並返回y(nlogn) int merge(int x, int y){ //返回存在的子節點。如果子節點都不存在,返回的是空結點 if (!x) return y; if (!y) return x; lc[y]=merge(lc[x], lc[y]); rc[y]=merge(rc[x], rc[y]); seg[y]=seg[lc[y]]+seg[rc[y]]; return y; } //在權值線段樹中找到第k個點(logn) int query(int now, int l, int r, int k){ if (l==r) return l; int mid=(l+r)>>1; if (seg[lc[now]]>=k) return query(lc[now], l, mid, k); else return query(rc[now], mid+1, r, k-seg[lc[now]]); } int main(){ get(n); get(m); for (int i=1; i<=n; ++i){ get(v[i]); id[v[i]]=i; fa[i]=i; root[i]=cnt++; add(root[i], 1, n, v[i]); } int t1, t2; for (int i=0; i<m; ++i){ get(t1); get(t2); merge(root[find(t1)], root[find(t2)]); fa[find(t1)]=find(t2); } get(q); char c; int x, y; for (int i=0; i<q; ++i){ while (!isgraph(c=getchar())); get(x); get(y); if (c=='Q'){ x=root[find(x)]; if (seg[x]<y) puts("-1"); else printf("%d\n", id[query(x, 1, n, y)]); } else { merge(root[find(x)], root[find(y)]); fa[find(x)]=find(y); } } return 0; }

luogu3224 永無鄉(動態開點,權值線段樹合並)