bzoj 3545/3551: [ONTAK2010]Peaks -- 主席树,最小生成树,倍增
Posted lkhll--qaz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 3545/3551: [ONTAK2010]Peaks -- 主席树,最小生成树,倍增相关的知识,希望对你有一定的参考价值。
3545: [ONTAK2010]Peaks
Time Limit: 10 Sec Memory Limit: 128 MBDescription
在Bytemountains有N座山峰,每座山峰有他的高度h_i。有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1。
Input
第一行三个数N,M,Q。
第二行N个数,第i个数为h_i
接下来M行,每行3个数a b c,表示从a到b有一条困难值为c的双向路径。
接下来Q行,每行三个数v x k,表示一组询问。
Output
对于每组询问,输出一个整数表示答案。
Sample Input
1 2 3 4 5 6 7 8 9 10
1 4 4
2 5 3
9 8 2
7 8 10
7 1 4
6 7 1
6 4 8
2 1 5
10 8 10
3 4 7
3 4 6
1 5 2
1 5 6
1 5 8
8 9 2
Sample Output
1
-1
8
HINT
【数据范围】
N<=10^5, M,Q<=5*10^5,h_i,c,x<=10^9。
Source
首先一定是走最小生成树上的边是最优的,然后我们按建树的顺序合并节点,这样我们发现一个子树里的边权都小于他本身
然后查询就是倍增找到当前点能到的最远的祖先,就变成了查询子树的第k大,然后用主席树维护就可以了
#include<map> #include<cmath> #include<queue> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define inf 1000000007 #define ll long long #define M 6000010 #define N 500010 inline int rd() { int x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} return x*f; } int n,m,Q; int h[N]; struct qaz{int a,b,c;}e[N],tp[N]; bool cmp(qaz a,qaz b){return a.c<b.c;} int fa[N],mz; int findf(int x){return x==fa[x]?x:fa[x]=findf(fa[x]);} int lj[N],fro[N],to[N],cnt; void add(int a,int b){fro[++cnt]=lj[a];to[cnt]=b;lj[a]=cnt;} bool vs[N]; int st[N],ed[N],tim,dep[N]; int Fa[200010][17],rt[N]; int ls[M],rs[M],sz[M],tot; void add(int lst,int &p,int l,int r,int x) { p=++tot; ls[p]=ls[lst];rs[p]=rs[lst]; sz[p]=sz[lst]+1; if(l==r) return; int mid=l+r>>1; if(x<=mid) add(ls[lst],ls[p],l,mid,x); else add(rs[lst],rs[p],mid+1,r,x); } void dfs(int x) { st[x]=++tim;vs[x]=1; dep[x]=dep[Fa[x][0]]+1; if(x<=n) add(rt[tim-1],rt[tim],1,n,h[x]); else rt[tim]=rt[tim-1]; for(int i=1;i<17;i++) { if(dep[x]<(1<<i)) break; Fa[x][i]=Fa[Fa[x][i-1]][i-1]; } for(int i=lj[x];i;i=fro[i]) { Fa[to[i]][0]=x; dfs(to[i]); } ed[x]=tim; } int fd(int x,int y,int l,int r,int k) { if(l==r) return tp[l].c; int mid=l+r>>1; if(sz[rs[x]]-sz[rs[y]]>=k) return fd(rs[x],rs[y],mid+1,r,k); else return fd(ls[x],ls[y],l,mid,k-sz[rs[x]]+sz[rs[y]]); } int main() { mz=n=rd();m=rd();Q=rd(); for(int i=1;i<=n;i++) tp[i].c=rd(),tp[i].a=i; sort(tp+1,tp+n+1,cmp); for(int i=1;i<=n;i++) { if(i>1&&tp[i].c==tp[i-1].c) h[tp[i].a]=h[tp[i-1].a]; else h[tp[i].a]=i; } for(int i=1,a,b,c;i<=m;i++) { a=rd();b=rd();c=rd(); e[i]=(qaz){a,b,c}; } sort(e+1,e+m+1,cmp); for(int i=1;i<n+n;i++) fa[i]=i; for(int i=1,x,y;i<=m;i++) { x=findf(e[i].a);y=findf(e[i].b); if(x==y) continue; fa[x]=fa[y]=++mz; h[mz]=e[i].c; add(mz,x);add(mz,y); if(mz==n*2-1) break; } for(int i=1;i<=n;i++) if(!vs[i]) dfs(findf(i)); int x,y,z,lans=-1,Rt; while(Q--) { x=rd();y=rd();z=rd(); Rt=x; for(int i=16;i>=0;i--) if(dep[Rt]>(1<<i)&&h[Fa[Rt][i]]<=y) Rt=Fa[Rt][i]; if(sz[rt[ed[Rt]]]-sz[rt[st[Rt]-1]]<z) lans=-1; else lans=fd(rt[ed[Rt]],rt[st[Rt]-1],1,n,z); printf("%d\n",lans); } return 0; }
以上是关于bzoj 3545/3551: [ONTAK2010]Peaks -- 主席树,最小生成树,倍增的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ 3545ONTAK 2010Peaks & BZOJ 3551ONTAK 2010Peaks加强版 Kruskal重构树
bzoj3545/bzoj3551 [ONTAK2010]Peaks/Peaks加强版
BZOJ4278[ONTAK2015]Tasowanie 后缀数组