P2502:[HAOI2006]旅行

Posted 秋千旁的蜂蝶~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2502:[HAOI2006]旅行相关的知识,希望对你有一定的参考价值。

题目描述

Z小镇是一个景色宜人的地方,吸引来自各地的观光客来此旅游观光。Z小镇附近共有N个景点(编号为1,2,3,…,N),这些景点被M条道路连接着,所有道路都是双向的,两个景点之间可能有多条道路。也许是为了保护该地的旅游资源,Z小镇有个奇怪的规定,就是对于一条给定的公路Ri,任何在该公路上行驶的车辆速度必须为Vi。速度变化太快使得游客们很不舒服,因此从一个景点前往另一个景点的时候,大家都希望选择行使过程中最大速度和最小速度的比尽可能小的路线,也就是所谓最舒适的路线。

输入输出格式

输入格式:
第一行包含两个正整数,N和M。

接下来的M行每行包含三个正整数:x,y和v。表示景点x到景点y之间有一条双向公路,车辆必须以速度v在该公路上行驶。

最后一行包含两个正整数s,t,表示想知道从景点s到景点t最大最小速度比最小的路径。s和t不可能相同。

输出格式:
如果景点s到景点t没有路径,输出“IMPOSSIBLE”。否则输出一个数,表示最小的速度比。如果需要,输出一个既约分数。

输入输出样例

输入样例#1:
4 2
1 2 1
3 4 2
1 4
输出样例#1:
IMPOSSIBLE

输入样例#2:
3 3
1 2 10
1 2 5
2 3 8
1 3
输出样例#2:
5/4

输入样例#3:
3 2
1 2 2
2 3 4
1 3
输出样例#3:
2

说明

【数据范围】

1<N≤500

1≤x,y≤N,0<v<30000,x≠y

0<M≤5000


题解

咋一看挺唬人的一道题(哎,图论总是我的痛……)
不过看看n、m这么小,可以想到把边权(Vi)从小到大排个序,从某条小边开始不断加边,直到s与t连通
如何判断是否连通呢——并查集
这样每次把“小边”往后推,再加边,s与t连通后边权差用最后加的那条边的边权-小边边权即可
由于并查集不支持删点,所以每次只能将小边往后推一个

这种想法很巧啊~


代码

#include<cstdio>
#include<iostream>
#include<algorithm>

#define INF 30000005
using namespace std;

const int N = 505;
const int M = 5005;

struct edge{
    int u,v,len;
    bool operator < (const edge &x) const{
        return x.len>len;     
    }
}h[M];

int fa[N];
int Getfa(int x){ return fa[x]==x?x:fa[x]=Getfa(fa[x]); }
void unit(int x,int y) { fa[Getfa(x)]=Getfa(y); }

int n,m,s,t;

int up,down=1;
double ans=INF;
int prime[5000],p[30005],num;
void Getprime(){
    for(int i=2;i<30005;i++) p[i]=1;
    for(int i=2;i<30005;i++){
        if(p[i]) prime[num++]=i;
        for(int j=0;j<num && (long long)prime[j]*i<30005;j++){
            p[prime[j]*i]=0;
            if(i%prime[j]==0) break;        
        }
    }
}

int main()
{
    int i,j;
    scanf("%d%d",&n,&m);
    for(i=0;i<m;i++)
        scanf("%d%d%d",&h[i].u,&h[i].v,&h[i].len);
    scanf("%d%d",&s,&t);
    
    sort(h,h+m);
    for(i=0;i<m;i++){
        for(j=1;j<=n;j++) fa[j]=j;
        for(j=i;j<m;j++){
            unit(h[j].u,h[j].v);
            if(Getfa(s)==Getfa(t)) break;                 
        }
        if(Getfa(s)==Getfa(t) && ((double)h[j].len/h[i].len)<ans) {
            up=h[j].len; down=h[i].len;
            ans=((double)up/down);
        }
    }
    
    if(ans==INF) printf("IMPOSSIBLE\n");
    else if((int)ans*down==up) printf("%.0lf\n",ans);
    else {
        Getprime();
        for(i=0;i<num && prime[i]<down;i++)
            while(up%prime[i]==0 && down%prime[i]==0) up/=prime[i],down/=prime[i];    
        printf("%d/%d\n",up,down);
    }
    
    return 0;    
}

以上是关于P2502:[HAOI2006]旅行的主要内容,如果未能解决你的问题,请参考以下文章

luogu P2502 [HAOI2006]旅行

luogu题解P2502[HAOI2006]旅行--最小生成树变式

P2502 [HAOI2006]鏃呰 鏈€灏忕敓鎴愭爲

bzoj1050HAOI2006旅行comf

bzoj 1050 [HAOI2006]旅行comf

[HAOI2006]旅行comf