COJ0573 [ONTAK2010]Peaks


试题描述

在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

COJ0573 [ONTAK2010]Peaks的相关文章

bzoj3545 [ONTAK2010]Peaks

3545: [ONTAK2010]Peaks Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 572  Solved: 164[Submit][Status] Description 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1. I

bzoj 3545&amp;&amp;3551: [ONTAK2010]Peaks &amp;&amp;加强版 平衡树&amp;&amp;并查集合并树&amp;&amp;主席树

3545: [ONTAK2010]Peaks Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 635  Solved: 177[Submit][Status] Description 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1. I

bzoj 3545/3551: [ONTAK2010]Peaks -- 主席树,最小生成树,倍增

3545: [ONTAK2010]Peaks Time Limit: 10 Sec  Memory Limit: 128 MB Description 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1. Input 第一行三个数N,M,Q.第二行N个数,第i个数为h_i接下来M行,每行

【bzoj3545】[ONTAK2010]Peaks 线段树合并

[bzoj3545][ONTAK2010]Peaks 2014年8月26日3,1512 Description 在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有一条困难

bzoj3545/bzoj3551 [ONTAK2010]Peaks/Peaks加强版

传送门:bzoj  bzoj wdnmd为什么加强版不是权限题原题却是啊 3545: [ONTAK2010]Peaks Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达

bzoj 3551: [ONTAK2010]Peaks加强版

Description [题目描述]同3545 Input 第一行三个数N,M,Q. 第二行N个数,第i个数为h_i 接下来M行,每行3个数a b c,表示从a到b有一条困难值为c的双向路径. 接下来Q行,每行三个数v x k,表示一组询问.v=v xor lastans,x=x xor lastans,k=k xor lastans.如果lastans=-1则不变. Output 同3545 Sample Input Sample Output HINT [数据范围]同3545 Source

bzoj3551 [ONTAK2010]Peaks加强版

Description //[题目描述]同3545 在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,表

【BZOJ 3551】[ONTAK2010] Peaks加强版 Kruskal重构树+树上倍增+主席树

这题真刺激...... I.关于Kruskal重构树,我只能开门了,不过补充一下那玩意还是一棵满二叉树.(看一下内容之前请先进门坐一坐) II.原来只是用树上倍增求Lca,但其实树上倍增是一种方法,Lca只是他的一种应用,他可以搞各种树上问题,树上倍增一般都会用到f数组. |||.我们跑出来dfs序就能在他的上面进行主席树了. IV.别忘了离散. V.他可能不连通,我一开始想到了,但是我觉得出题人可能会是好(S)人(B),但是...... #include <cstdio> #include

[BZOJ3545] [ONTAK2010]Peaks(线段树合并 + 离散化)

传送门 由于困难值小于等于x这个很恶心,可以离线处理,将边权,和询问时的x排序. 每到一个询问的时候,将边权小于等于x的都合并起来再询问. .. 有重复元素的线段树合并的时间复杂度是nlog^2n #include <cstdio> #include <iostream> #include <algorithm> #define N 500001 int n, m, q, cnt, tot, size; int sum[N * 10], ls[N * 10], rs[N