「BZOJ3545」「ONTAK2010」 Peaks - Splay啟發式合併
阿新 • • 發佈:2019-01-01
題目描述
在Bytemountains有N座山峰,每座山峰有它的高度h_i。有些山峰之間有雙向道路相連,共M條路徑,每條路徑有一個困難值,這個值越大表示越難走,現在有Q組詢問,每組詢問詢問從點v開始只經過困難值小於等於x的路徑所能到達的山峰中第k高的山峰,如果無解輸出-1。
輸入格式
第一行三個數N,M,Q。
第二行N個數,第i個數為h_i。
接下來M行,每行3個數a b c,表示從a到b有一條困難值為c的雙向路徑。
接下來Q行,每行三個數v x k,表示一組詢問。
輸出格式
對於每組詢問,輸出一個整數表示所求山峰的高度。
資料範圍
N<=105
分析
由於題目沒有要求要線上,於是考慮離線。讀入所有的邊和詢問,將邊和詢問分別按照 和 從小到大排序。然後對每個點開 棵 ,對於每個詢問,將所有 的邊的兩頂點的 合併起來,然後在 所在的 中查詢第 大。由於先排序了的,所以順序處理每個詢問,在上一個詢問的基礎上合併。
程式碼
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N=100005,M=500005;
typedef long long LL;
struct Edge {
int x,y,w;
bool operator<(const Edge&e) const {
return w<e.w;
}
}e[M];
struct Query {
int v,x,k,id;
bool operator<(const Query&e) const {
return x<e.x;
}
}q[M];
int n,m,Q,sz[N];
int v[N],f[N],c[N][2],ans[M];
int fa[N],pt[N],st[N],top;
void Pushup(int x) {if (x) sz[x]=sz[c[x][0]]+sz[c[x][1]]+1;}
int Getf(int x) {return fa[x]==x?x:fa[x]=Getf(fa[x]);}//用並查集判斷聯通性
void Rotate(int x) {
int y=f[x],z=f[y];
int k=c[y][1]==x,kk=c[z][1]==y;
c[y][k]=c[x][k^1];
f[c[x][k^1]]=y;
c[x][k^1]=y;
f[y]=x;
if (z) c[z][kk]=x;
f[x]=z;
Pushup(y);
Pushup(x);
}
void Splay(int x) {
while (f[x]) {
int y=f[x],z=f[y];
if (z) {
if ((c[y][1]==x)^(c[z][1]==y)) Rotate(x);
else Rotate(y);
}
Rotate(x);
}
}
void Insert(int p,int q) {
while (1) {
sz[p]++;
int nxt=(v[q]<=v[p]?0:1);
if (!c[p][nxt]) {
c[p][nxt]=q;
f[q]=p;
c[q][0]=c[q][1]=0;
sz[q]=1;
Splay(q);
return;
}
p=c[p][nxt];
}
}//以上Splay基本操作
void Dfs(int x) {
if (!x) return;
Dfs(c[x][0]);
st[++top]=x;
Dfs(c[x][1]);
}
void Merge(int x,int y) {
int fx=Getf(x);
int fy=Getf(y);
if (fx==fy) return;
fa[fy]=fx;
Splay(x);
Splay(y);
if (sz[x]<sz[y]) swap(x,y);//啟發式,將小的向大的合併
Dfs(y);//獲得中序遍歷序,據說可以減少時間複雜度
st[top+1]=x;
while (top) {
Insert(st[top+1],st[top]);//一個個插入
top--;
}
}
int Kth(int p,int k) {
Splay(p);
if (k>sz[p]) return -1;
k=sz[p]-k+1;
while (1) {
if (sz[c[p][0]]+1==k) return v[p];
if (sz[c[p][0]]>=k) p=c[p][0];
else {
k-=sz[c[p][0]]+1;
p=c[p][1];
}
}
}
int main() {
scanf("%d%d%d",&n,&m,&Q);
for (int i=1;i<=n;i++) scanf("%d",&v[i]),fa[i]=i,sz[i]=1;//讀入並初始化
for (int i=1;i<=m;i++) scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w);
for (int i=1;i<=Q;i++) scanf("%d%d%d",&q[i].v,&q[i].x,&q[i].k),q[i].id=i;
sort(e+1,e+m+1);
sort(q+1,q+Q+1);
int ei,qi;
ei=qi=1;
while (qi<=Q) {
while (ei<=m&&e[ei].w<=q[qi].x) {
Merge(e[ei].x,e[ei].y);
ei++;
}
ans[q[qi].id]=Kth(q[qi].v,q[qi].k);
qi++;
}
for (int i=1;i<=Q;i++) printf("%d\n",ans[i]);
return 0;
}