XJOI 修缮计划(最小生成树,LCA)

题面非常简单,给你一张图

一条路径的权是指路径上最长边的权值

询问两个点之间所有路径中权值最小的

然后我傻乎乎地打了一个暴力SPFA

#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int N=10010,M=200010;
int dis[N][N],b[M],c[M],ne[M],fi[N],cc[N],k,x,y,in[N],n,m,z,q;
inline void add(const int &x,const int &y,const int &z){
    b[++k]=y; c[k]=z; ne[k]=fi[x]; fi[x]=k;
}
inline int max(const int &x,const int &y){
    return x>y?x:y;
}
void SPFA(int s){
    queue<int>q;
    q.push(s);
    dis[s][s]=0;
    while (!q.empty()){
        x=q.front(); q.pop(); in[x]=0;
        for (int j=fi[x]; j; j=ne[j])
        if (max(dis[s][x],c[j])<dis[s][b[j]]){
            dis[s][b[j]]=max(dis[s][x],c[j]);
            if (!in[b[j]]){
                q.push(b[j]);
                in[b[j]]=1;
            }
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for (int i=1; i<=n; i++) scanf("%d",&cc[i]);
    for (int i=1; i<=m; i++){
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z+cc[x]+cc[y]); add(y,x,z+cc[x]+cc[y]);
    }
    memset(dis,0x3f3f3f,sizeof(dis));
    for (int i=1; i<=n; i++) SPFA(i);
    scanf("%d",&q);
    while (q--){
        scanf("%d%d",&x,&y);
        printf("%d\n",dis[x][y]);
    }
}

考试结束前10分钟才意识到正解,为时已晚.

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int N=10010,M=200010,INF=0x3f3f3f;
struct edge{
    int x,y,z;
}e[M];
int b[M],c[M],ne[M],fi[N],cc[N],fa[N],k,x,y,n,m,z,q,f[N][21],maxf[N][21],deep[N];
inline void add(const int &x,const int &y,const int &z){
    b[++k]=y; c[k]=z; ne[k]=fi[x]; fi[x]=k;
}
bool cmp(edge x,edge y){
    return x.z<y.z;
}
inline int find(int x){
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
void dfs(int x){
    for (int j=fi[x]; j; j=ne[j])
    if (b[j]!=f[x][0]){
        f[b[j]][0]=x;
        maxf[b[j]][0]=c[j];
        deep[b[j]]=deep[x]+1;
        dfs(b[j]);
    }
}
int getlca(int x,int y){
    int res=0;
    if (deep[x]>deep[y]) x^=y^=x^=y;
    for (int j=20; j>=0; j--)
    if (deep[f[y][j]]>=deep[x]){
        res=max(res,maxf[y][j]);
        y=f[y][j];
    }
    if (x==y) return res;
    for (int j=20; j>=0; j--)
    if (f[x][j]!=f[y][j]){
        res=max(max(res,maxf[x][j]),maxf[y][j]);
        x=f[x][j];
        y=f[y][j];
    }
    res=max(res,max(maxf[x][0],maxf[y][0]));
    return res;
}
int main(){
    scanf("%d%d",&n,&m);
    for (int i=1; i<=n; i++) scanf("%d",&cc[i]);
    for (int i=1; i<=m; i++){
        scanf("%d%d%d",&x,&y,&z);
        e[i].x=x;
        e[i].y=y;
        e[i].z=z+cc[x]+cc[y];
    }
    sort(e+1,e+m+1,cmp);
    for (int i=1; i<=n; i++) fa[i]=i;
    for (int i=1; i<=m; i++){
        x=find(e[i].x); y=find(e[i].y);
        if (x!=y){
            fa[x]=y;
            add(e[i].x,e[i].y,e[i].z);
            add(e[i].y,e[i].x,e[i].z);
        }
    }
    dfs(1);
    for (int j=1; j<=20; j++)
    for (int i=1; i<=n; i++){
        f[i][j]=f[f[i][j-1]][j-1];
        maxf[i][j]=max(maxf[i][j-1],maxf[f[i][j-1]][j-1]);
    }
    scanf("%d",&q);
    while (q--){
        scanf("%d%d",&x,&y);
        printf("%d\n",getlca(x,y));
    }
}

惨啊

时间: 2024-10-11 05:40:59

XJOI 修缮计划(最小生成树,LCA)的相关文章

UVa 11354 Bond 最小生成树+LCA倍增

题目来源:UVa 11354 Bond 题意:n个点m条边的图 q次询问 找到一条从s到t的一条边 使所有边的最大危险系数最小 思路:使最大的危险系数尽量小 答案是最小生成树上的边 然后用LCA倍增法记录s和t到他们最近公共祖先的最大值 #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 50010; const int INF =

Vijos[1983]NOIP2015Day2T3 运输计划 transport LCA

题目链接Vijos 题目链接UOJ 转载一个大佬的题解: 点击这里->银牌爷题解 主要考察二分查找.树上倍增.贪心."树上前缀和".题目是一颗树,要求将一条边的权值变为0,使得所有运输计划的最大时间最小.直觉告诉我们,这是一个树上倍增的题目,但是它却不像前几年的 Day2T3 开车旅行那样纯倍增,或许更像疫情控制一些,倍增只是辅助算法,还需要配合其他算法.由于要使所有运输计划的最大时间最小,不难想到二分答案的方法.使C(t)表示是否可以改造一条边,使得改造之后所有运输计划中最长的

UVAlive 6622 Absurdistan Roads(最小生成树+LCA)

题目地址:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4633 思路:每个点之间连边,权值为两点之间的最短距离.则该图的最小生成树的n-1条边在最终的n条边内.则两点(i,j)之间距离为dist[i]+dist[j]-2*dist[ LCA(i,j) ](dist[i]表示根节点(设为1)到i节点的距离).若树上每点之间的

noip 2015 运输计划 (lca+二分)

/* 95 最后一个点T了 qian lv ji qiong 了 没学过树剖 听chx听xzc说的神奇的方法 Orz 首先求出每个计划的路径长度 这里写的倍增 然后二分答案 对于每个ans 统计>他的路径条数 tot 并维护最大差值 dec 并且对于每条不合法的路径维护每个点的经过次数 然后枚举点 如果经过次数==tot说明每一条不合法的都经过他 然后尝试把它建成虫洞 如果他对应边的权值>=dec 那么我们删掉它ans就合法了 关键是统计每个点在非法路径中的经过次数 : 维护sum数组 对于每

UVA - 11354 Bond(最小生成树+LCA+瓶颈路)

题意:N个点,M条路,每条路的危险度为路上各段中最大的危险度.多组询问,点s到点t的所有路径中最小的危险度. 分析: 1.首先建个最小生成树,则s到t的路径一定是危险度最小的. 原因:建最小生成树的最后一步,如果有两个相等的边可以选择,然后将两个连通块连在一起. 那不管选择哪个边,对于分别位于两个连通块的两点来说,这条边都是必经之路,而这个必经之路是这两点路径的危险度中最大的,起决定作用,所以选哪个是一样的. 2.利用lca,在找s和t最近公共祖先的过程中,不断取两者路径中的最大危险度即可. 3

UVA 11354 Bond(最小生成树+lca+倍增求祖先节点)

题意:n个点m条边,每条边有一个权值,有q个询问,每次询问两点间的一条路径,使得这条路径上权值最大的边最小. 思路:很容易想到最小瓶颈路,但是查询太多,会超时,可以预处理出最小生成树,则问题转化为一棵树上的两点间路径中权值最大的那条边,设这两点为u,v,可以得到dist(u,v)=max(dist(u,lca(u,v)),dist(v,lca(v,lca))),其中lca(u,v)表示u和v的最近公共祖先,用倍增的思想预处理出每个结点的2^i的祖先fa[u][i],然后在离线求出lca后顺便计算

UVA 11354 Bond 瓶颈路 最小生成树+LCA类似

题目链接:点击打开链接 题意: 给定n个点m条边的无向图 下面m行是(u,v) 和边权 下面q个询问 (u, v) 在这两个点间找一条路径使得这个路径上最大的边权最小. 数据保证询问的2个点之间一定存在路径 思路: 求瓶颈路,最小生成树跑一下. 然后求lca的代码里加入边权. 因为要使得最大的边权最小,所以用最小生成树的krusal算法, 正确性证明: 我们现在有联通块G,一个孤立点u,以及G与u之间的一些边,则显然我们选择边权最小的一条边把2个联通块连接. 这个过程就是krusal. #inc

UVA - 11354Bond最小生成树,LCA寻找最近公共祖先

/* Author: 2486 Memory: 0 KB Time: 2222 MS Language: C++11 4.8.2 Result: Accepted VJ RunId: 4236841 Real RunId: 15859210 */ #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include

bzoj3732: Network(最小生成树+LCA)

3732: Network 题目:传送门 题解: 第一眼就看到最大边最小,直接一波最小生成树. 一开始还担心会错,问了一波肉大佬,任意两点在最小生成树上的路径最大边一定是最小的. 那么事情就变得简单起来了嘿嘿嘿,建棵树,直接在线LCA啊,用一个mx[i][j]记录i往上2^j这段区间的最大值. 代码: 1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath> 5