题目描述
在 Bytemountains 有 N N N 座山峰,每座山峰有他的高度 h i h_i hi。有些山峰之间有双向道路相连,共 M M M 条路径,每条路径有一个困难值,这个值越大表示越难走,现在有 Q Q Q 组询问,每组询问询问从点 v v v 开始只经过困难值小于等于 x x x 的路径所能到达的山峰中第 k k k 高的山峰,如果无解输出 − 1 -1 −1。 N ≤ 1 0 5 N\le 10^5 N≤105, M , Q ≤ 5 × 1 0 5 M,Q\le 5\times 10^5 M,Q≤5×105, h i , c , x ≤ 1 0 9 h_i,c,x\le 10^9 hi,c,x≤109。
算法分析
学习了一下 Kruskal 重构树,本题利用了 Kruskal 重构树的堆性质,只需要树上倍增找到能走到的最接近根节点的祖先,所有能到达的山峰就是其所在子树的所有叶子节点,可以用 DFS 序+主席树维护子树中的叶子节点权值。
细节有主席树求区间第 k k k 小而本题要求区间第 k k k 大,需要简单转化一下,还有注意维护的是子树中叶子节点的权值。还有个细节:原来的节点数不一定等于 ⌊ n 2 ⌋ + 1 \lfloor\frac{n}{2}\rfloor+1 ⌊2n⌋+1,因为图不一定连通吧。
【BZOJ 3545】Peaks 为不强制在线的版本,双倍经验哦。
代码实现
#include <cstdio>
#include <algorithm>
const int maxn=(int)1e5+5;
const int maxm=(int)5e5+5;
char buf[1<<15],*fs=buf,*ft=buf;
inline char gc() {
if(fs==ft) {
ft=(fs=buf)+fread(buf,1,1<<15,stdin);
if(fs==ft) return 0;
}
return *fs++;
}
inline void read(int &num) {
char c=gc();int f=false;num=0;
while(c<'0'||'9'<c) {if(c=='-') f=true;c=gc();}
while('0'<=c&&c<='9') {num=num*10+c-'0';c=gc();}
if(f) num=-num;
}
int sum[maxn<<6],lch[maxn<<6],rch[maxn<<6],cnt=0;
int ins(int o,int l,int r,int x) {
int mid=(l+r)>>1,nxt=++cnt;
if(l==r) sum[nxt]=sum[o]+1;
else {
lch[nxt]=(x<=mid)?ins(lch[o],l,mid,x):lch[o];
rch[nxt]=(mid+1<=x)?ins(rch[o],mid+1,r,x):rch[o];
sum[nxt]=sum[lch[nxt]]+sum[rch[nxt]];
}
return nxt;
}
int query(int x,int y,int l,int r,int k) {
int mid=(l+r)>>1;if(l==r) return mid;
int lcnt=sum[lch[y]]-sum[lch[x]];
if(k<=lcnt) return query(lch[x],lch[y],l,mid,k);
return query(rch[x],rch[y],mid+1,r,k-lcnt);
}
struct edge {int u,v,w;} e[maxm];
inline bool cmp(const edge &x,const edge &y) {return x.w<y.w;}
int head[maxn<<1],ev[maxn<<1],nxt[maxn<<1],idx=0;
inline void add(int u,int v) {ev[++idx]=v;nxt[idx]=head[u];head[u]=idx;}
int fa[maxn<<1];int find(int x) {return x==fa[x]?x:fa[x]=find(fa[x]);}
int ha[maxn],sz=0;inline int ask(int x) {return std::lower_bound(ha,ha+sz,x)-ha+1;}
int h[maxn<<1],pa[maxn<<1][20],rt[maxn<<1],tot[maxn<<1],dl[maxn<<1],dfn[maxn<<1],dfsidx=0;
void dfs(int x,int fa) {
dl[dfn[x]=++dfsidx]=x;tot[x]=1;
for(register int i=head[x];i;i=nxt[i]) {
int v=ev[i];dfs(v,x);tot[x]+=tot[v];
}
}
int main() {
int ln,n,m,q;read(n);ln=n;read(m);read(q);
for(register int i=1;i<=n;++i) {read(h[i]);ha[i-1]=h[i];}
std::sort(ha,ha+n);sz=std::unique(ha,ha+n)-ha;
for(register int i=0;i<m;++i) {read(e[i].u);read(e[i].v);read(e[i].w);}
std::sort(e,e+m,cmp);
for(register int i=1;i<=(n<<1);++i) fa[i]=i;
for(register int i=0;i<m;++i) {
int x=find(e[i].u),y=find(e[i].v);if(x==y) continue;
++n;h[n]=e[i].w;pa[x][0]=pa[y][0]=fa[x]=fa[y]=n;add(n,x);add(n,y);
}
for(register int i=1;i<20;++i) for(register int x=1;x<=n;++x) pa[x][i]=pa[pa[x][i-1]][i-1];
dfs(n,0);for(register int i=1;i<=n;++i) rt[i]=(dl[i]<=ln)?ins(rt[i-1],1,sz,ask(h[dl[i]])):rt[i-1];
int v,x,k,la=0;
while(q--) {
read(v);read(x);read(k);if(~la) {v^=la;x^=la;k^=la;}
for(int i=19;i>=0;--i) if(pa[v][i]&&h[pa[v][i]]<=x) v=pa[v][i];
int l=dfn[v],r=dfn[v]+tot[v]-1,cnt=sum[rt[r]]-sum[rt[l-1]];
printf("%d\n",la=(k<=cnt?ha[query(rt[l-1],rt[r],1,sz,cnt+1-k)-1]:-1));
}
return 0;
}