试题描述 |
在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,表示一组询问。 |
输出 |
对于每组询问,输出一个整数表示答案。 |
输入示例 |
10 11 4 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 |
输出示例 |
6 1 -1 8 |
其他说明 |
N<=100000, M,Q<=500000,h_i,c,x<=10^9 |
在线算法:
做一次最小生成树,考虑在连边x,y时新建节点ToT,连接ToT->findset(x),ToT->findset(y),将ToT的权值赋为边(x,y)的权值
这样有什么好处呢?对于x走不超过v的边能到达的节点就是x向上倍增最上面一个rt的子树的叶结点,DFS序+主席树就可以做k大了。
这是正常版的
#include<cstdio> #include<cctype> #include<queue> #include<cstring> #include<algorithm> #define rep(s,t) for(int i=s;i<=t;i++) #define ren for(int i=first[x];i;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c==‘-‘) f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-‘0‘; return x*f; } inline void print(int x) { if(x==0){putchar(‘0‘);return;}if(x<0) putchar(‘-‘),x=-x; int len=0,buf[15];while(x) buf[len++]=x%10,x/=10; for(int i=len-1;i>=0;i--) putchar(buf[i]+‘0‘);putchar(‘\n‘); } const int maxn=200010; const int maxm=500010; const int maxnode=5000010; struct Edge { int u,v,w; bool operator < (const Edge& ths) const { return w<ths.w; } }e[maxm]; int n,m,q,ToT,h[maxn],val[maxn],pa[maxn]; int lastans,tmp[maxn]; int findset(int x) {return x==pa[x]?pa[x]:pa[x]=findset(pa[x]);} int first[maxn],next[maxn],to[maxn],es; void AddEdge(int u,int v) { to[++es]=v;next[es]=first[u];first[u]=es; } int fa[maxn][20],Ln[maxn],Rn[maxn],vis[maxn],sz; int root[maxn<<1],ls[maxnode],rs[maxnode],s[maxnode],TOT; void update(int& y,int x,int l,int r,int pos) { s[y=++TOT]=s[x]+1;if(l==r) return; int mid=l+r>>1;ls[y]=ls[x];rs[y]=rs[x]; if(pos<=mid) update(ls[y],ls[x],l,mid,pos); else update(rs[y],rs[x],mid+1,r,pos); } int query(int x,int y,int l,int r,int k) { if(l==r) return l; int k2=s[rs[y]]-s[rs[x]],mid=l+r>>1; if(k2>=k) return query(rs[x],rs[y],mid+1,r,k); return query(ls[x],ls[y],l,mid,k-k2); } void dfs(int x) { Ln[x]=++sz;vis[x]=1; if(x<=n) update(root[sz],root[sz-1],1,n,lower_bound(tmp+1,tmp+n+1,h[x])-tmp); else root[sz]=root[sz-1]; rep(1,19) fa[x][i]=fa[fa[x][i-1]][i-1]; ren fa[to[i]][0]=x,dfs(to[i]); Rn[x]=++sz;root[sz]=root[sz-1]; } int findrt(int x,int v) { for(int i=19;i>=0;i--) if(val[fa[x][i]]<=v) x=fa[x][i]; return x; } int main() { val[0]=2e9; n=ToT=read();m=read();q=read(); rep(1,n) tmp[i]=h[i]=read(),pa[i]=i; sort(tmp+1,tmp+n+1); rep(1,m) e[i].u=read(),e[i].v=read(),e[i].w=read(); sort(e+1,e+m+1); rep(1,m) { int u=findset(e[i].u),v=findset(e[i].v); if(u!=v) { val[++ToT]=e[i].w;pa[ToT]=ToT; AddEdge(ToT,u);AddEdge(ToT,v); pa[u]=pa[v]=ToT; } } rep(1,n) if(!vis[i]) dfs(findset(i)); rep(1,q) { int v=read(),x=read(),k=read(); int rt=findrt(v,x),r1=root[Ln[rt]-1],r2=root[Rn[rt]]; if(s[r2]-s[r1]<k) print(lastans=-1); else print(lastans=tmp[query(r1,r2,1,n,k)]); } return 0; }
这是自己写人工栈版的(本机第一种写法会爆栈,无法出数据)
#include<cstdio> #include<cctype> #include<queue> #include<stack> #include<cstring> #include<algorithm> #define rep(s,t) for(int i=s;i<=t;i++) #define ren for(int i=first[x];i;i=next[i]) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c==‘-‘) f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-‘0‘; return x*f; } const int maxn=200010; const int maxm=500010; const int maxnode=7000010; struct Edge { int u,v,w; bool operator < (const Edge& ths) const { return w<ths.w; } }e[maxm]; int n,m,q,ToT,h[maxn],val[maxn],pa[maxn]; int lastans,tmp[maxn]; int findset(int x) {return x==pa[x]?pa[x]:pa[x]=findset(pa[x]);} int first[maxn],next[maxn],to[maxn],es; void AddEdge(int u,int v) { to[++es]=v;next[es]=first[u];first[u]=es; } int fa[maxn][20],Ln[maxn],Rn[maxn],vis[maxn],sz; int root[maxn<<1],ls[maxnode],rs[maxnode],s[maxnode],TOT; void update(int& y,int x,int l,int r,int pos) { s[y=++TOT]=s[x]+1;if(l==r) return; int mid=l+r>>1;ls[y]=ls[x];rs[y]=rs[x]; if(pos<=mid) update(ls[y],ls[x],l,mid,pos); else update(rs[y],rs[x],mid+1,r,pos); } int query(int x,int y,int l,int r,int k) { if(l==r) return l; int k2=s[rs[y]]-s[rs[x]],mid=l+r>>1; if(k2>=k) return query(rs[x],rs[y],mid+1,r,k); return query(ls[x],ls[y],l,mid,k-k2); } stack<int> S; void dfs(int x) { S.push(x); while(!S.empty()) { x=S.top(); if(!vis[x]) { vis[x]=1;Ln[x]=++sz; if(x<=n) update(root[sz],root[sz-1],1,n,lower_bound(tmp+1,tmp+n+1,h[x])-tmp); else root[sz]=root[sz-1]; rep(1,19) fa[x][i]=fa[fa[x][i-1]][i-1]; ren fa[to[i]][0]=x,S.push(to[i]); } else Rn[x]=++sz,root[sz]=root[sz-1],S.pop(); } } int findrt(int x,int v) { for(int i=19;i>=0;i--) if(val[fa[x][i]]<=v) x=fa[x][i]; return x; } int main() { val[0]=2e9; n=ToT=read();m=read();q=read(); rep(1,n) tmp[i]=h[i]=read(),pa[i]=i; sort(tmp+1,tmp+n+1); rep(1,m) e[i].u=read(),e[i].v=read(),e[i].w=read(); sort(e+1,e+m+1); rep(1,m) { int u=findset(e[i].u),v=findset(e[i].v); if(u!=v) { val[++ToT]=e[i].w;pa[ToT]=ToT; AddEdge(ToT,u);AddEdge(ToT,v); pa[u]=pa[v]=ToT; } } rep(1,n) if(!vis[i]) dfs(findset(i)); rep(1,q) { int v=read(),x=read(),k=read(); int rt=findrt(v,x),r1=root[Ln[rt]-1],r2=root[Rn[rt]]; if(s[r2]-s[r1]<k) printf("%d\n",lastans=-1); else printf("%d\n",lastans=tmp[query(r1,r2,1,n,k)]); } return 0; }
离线算法:
考虑将操作与边升序排序,离线用平衡树维护每个连通块的答案,连边时启发式合并即可
时间: 2024-09-29 23:00:14