17AHU排位赛2 A题(最小生成树、LCA维护树上路径)

来源:互联网 发布:龙门县平陵镇网络问政 编辑:程序博客网 时间:2024/06/05 17:15

problem

有一个n个点m条边的连通无向图(无重边、无自环),点的编号为1~n。每条边都有一个正的边权,并且每条边边权互不相同(即数据保证该图的最小生成树唯一)。
如果只是求最小生成树,那么Ohyee觉得太简单了,于是他决定考考你。
对于每条边(u,v)都进行询问:
——如果该边在最小生成树上,那么输出“Ohyee”;
——如果该边不在最小生成树上,那么输出最小生成树上u,v两点路径中边权的最小值。

Input

第一行输入两个整数n,m(2<=n<=100000,n-1<=m<=200000)
接下来m行,每行输入三个整数u,v,w。u,v表示该边连接点的编号,w是边权。
(1<=w<=1000000000)

Output

输出一共m行,按照输入的边的顺序回答每条边的询问。

Input

4 4
1 2 2
2 3 5
3 4 7
4 1 3

Output

Ohyee
Ohyee
2
Ohyee

Limitation

1s 256MB

Hint

原图最小生成树中的边是(1,2,2) (2,3,5) (4,1,3)
对于输入的第三条边(3,4,7),树上3->4的路径是3->2->1->4,边权分别是5,2,3,最小值是2。


思路

最小生成树和树上路径信息维护的结合,两个模板套在一起即可


代码示例

#include<bits/stdc++.h>using namespace std;const int maxn=1e5+50;const int maxm=2e5+50;const int inf=0x3fffffff;int n,m;//n为点数 m为边数struct eee{//解决lca    int from,to,dist;    eee(int u,int v,int w):from(u),to(v),dist(w){    }};vector<eee> edges;//边的具体信息vector<int> GG[maxn];//边的编号void addEdge(int u,int v,int w){//vector邻接表    edges.push_back(eee(u,v,w));    edges.push_back(eee(v,u,w));    int size=edges.size();    GG[u].push_back(size-2);    GG[v].push_back(size-1);}const int maxlog=30;int grand[maxn][maxlog];int gmax[maxn][maxlog];int depth[maxn];int s;//倍增最大步数int root;//根节点void dfs(int x)//预处理{    for(int i=1;i<=s;++i){        grand[x][i]=grand[grand[x][i-1]][i-1];        gmax[x][i]=max(gmax[x][i-1],gmax[grand[x][i-1]][i-1]);        if(!grand[x][i]) break;    }    for(int i=0;i<GG[x].size();i++){        eee & e=edges[GG[x][i]];        if(e.to!=grand[x][0]){            depth[e.to]=depth[x]+1;            grand[e.to][0]=x;            gmax[e.to][0]=e.dist;            dfs(e.to);        }    }}void init(){    s=floor(log(n+0.0)/log(2.0));    depth[0]=-1;    //memset(depth,0,sizeof(depth));    memset(grand,0,sizeof(grand));    memset(gmax,0,sizeof(gmax));    root=1;    dfs(root);//以1为根结点建树}int lca(int a,int b,int &maxx)//最大值{    if(depth[a]>depth[b]) swap(a,b);    maxx=gmax[b][0];//之前的bug,应该放更深的b    int dre=depth[b]-depth[a];    for(int i=s;i>=0;--i){        if(dre&(1<<i)) maxx=max(maxx,gmax[b][i]),b=grand[b][i];    }    if(a==b) return a;    for(int i=s;i>=0;i--)        if(grand[a][i]!=grand[b][i]){            maxx=max(maxx,gmax[a][i]),maxx=max(maxx,gmax[b][i]);            a=grand[a][i],b=grand[b][i];        }    maxx=max(maxx,gmax[a][0]);    maxx=max(maxx,gmax[b][0]);    return grand[a][0];}typedef struct{//边集数组    int beg;    int endd;    int weight;    int flag;//1表示在 0表示不在    int order;//进来时的顺序}Edge;bool cmp(Edge a,Edge b)//边集数组排序{    return a.weight<b.weight;}bool cmp1(Edge a,Edge b)//原位置排序{    return a.order<b.order;}typedef struct{    Edge xiang[maxm];//边的信息    int numVertexes,numEdges;//顶点数和边数}MGraph;MGraph G;void CreateMGraph(){    for(int i=0;i<m;++i){        cin>>G.xiang[i].beg>>G.xiang[i].endd>>G.xiang[i].weight;        G.xiang[i].order=i;    }    G.numEdges=m;    G.numVertexes=n;    sort(G.xiang,G.xiang+m,cmp);}int parent[maxm];//辅助并查集int Find(int f)//查找连线顶点的尾部下标{    return parent[f]!=f?parent[f]=Find(parent[f]):f;}void MiniSpanTree_Kruskal(){    int nn,mm;    //int parent[maxm];//定义一数组用来判断边与边是否形成环路    for(int i=0;i<G.numVertexes;++i)        parent[i]=i;//初始化数组值为i    for(int i=0;i<G.numEdges;++i){//循环每一条边        nn=Find(G.xiang[i].beg);        mm=Find(G.xiang[i].endd);        if(nn!=mm){//假如n与m不等,说明此边没有与现有生成树形成环路            parent[nn]=mm;//将此边的结尾顶点放入下标为起点的parent中            //表示此顶点已经在生成树集合中            //printf("在:(%d,%d) %d\n",G.xiang[i].beg,G.xiang[i].endd,G.xiang[i].weight);            //sum+=G.xiang[i].weight;            G.xiang[i].flag=1;            addEdge(G.xiang[i].beg,G.xiang[i].endd,1000000001-G.xiang[i].weight);//进入lca,注意求最小值,所以拿大值减后求最大值            //cout<<G.xiang[i].beg<<' '<<G.xiang[i].endd<<' '<<1000000001-G.xiang[i].weight<<endl;        }        else{            G.xiang[i].flag=0;//不在        }        //else printf("不在:(%d,%d) %d\n",G.xiang[i].beg,G.xiang[i].endd,G.xiang[i].weight);    }    //cout<<sum<<endl;//路径和}int main(){    ios::sync_with_stdio(false);    cin>>n>>m;    CreateMGraph();    //for(int i=0;i<G.numEdges;++i) cout<<G.xiang[i].beg<<" "<<G.xiang[i].endd<<" "<<G.xiang[i].weight<<endl;    //cout<<G.numEdges<<G.numVertexes<<endl;    MiniSpanTree_Kruskal();    //for(int i=0;i<G.numVertexes;++i)    //cout<<parent[i]<<' ';    sort(G.xiang,G.xiang+m,cmp1);    init();    int maxx;//生成树上两点间最短边权    for(int i=0;i<m;++i){        if(G.xiang[i].flag==1){            cout<<"Ohyee"<<endl;        }        else{            lca(G.xiang[i].beg,G.xiang[i].endd,maxx);            //cout<<lca(u,v,maxx,sum)<<endl;            cout<<1000000001-maxx<<endl;//再减去即为最小值        }    }    return 0;}
阅读全文
1 0