• [BZOJ1050] [HAOI2006] 旅行comf (Kruskal, LCT)


    Description

      给你一个无向图,N(N<=500)个顶点, M(M<=5000)条边,每条边有一个权值Vi(Vi<30000)。给你两个顶点S和T
    ,求一条路径,使得路径上最大边和最小边的比值最小。如果S和T之间没有路径,输出”IMPOSSIBLE”,否则输出
    这个比值,如果需要,表示成一个既约分数。 备注: 两个顶点之间可能有多条路径。

    Input

      第一行包含两个正整数,N和M。下来的M行每行包含三个正整数:x,y和v。表示景点x到景点y之间有一条双向
    公路,车辆必须以速度v在该公路上行驶。最后一行包含两个正整数s,t,表示想知道从景点s到景点t最大最小速
    度比最小的路径。s和t不可能相同。
      1<N<=500,1<=x,y<=N,0<v<30000,0<M<=5000,可能出现自环

    Output

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

    Sample Input

    【样例输入1】
    4 2
    1 2 1
    3 4 2
    1 4
    【样例输入2】
    3 3
    1 2 10
    1 2 5
    2 3 8
    1 3
    【样例输入3】
    3 2
    1 2 2
    2 3 4
    1 3

    Sample Output

    【样例输出1】
    IMPOSSIBLE
    【样例输出2】
    5/4
    【样例输出3】
    2

    HINT

    Source

    Solution

      首先把边按边权排序

      第一种方法:枚举第一条边是哪条,之后从这条边开始做$Kruskal$,直到$S$与$T$联通或所有边都用完

      因为最小生成树可以保证最大边权尽量小,所以在最小边权指定的情况下可以找到比值最小的情况,复杂度$O(m^2)$

      ($Kruskal$不好玩,我们来玩$LCT$吧)

      第二种方法:我们用$LCT$维护最小生成树,按顺序插入边,当新插入的边的两端已经在树上时,把边权最小的边断开,再把这条边插进去

      联通性什么的都挺好判断的不用我多说了吧,复杂度是$O(mlog^2n)$的

      (好像有一些细节没讲,算了不管啦)

      1 #include <bits/stdc++.h>
      2 using namespace std;
      3 struct edge
      4 {
      5     int u, v, w;
      6     bool operator< (const edge &rhs) const
      7     {
      8         return w < rhs.w;
      9     }
     10 }e[5555];
     11 struct LCT
     12 {
     13     int c[2], fa, rev, val, mn, mx;
     14     int& operator[] (int x)
     15     {
     16         return c[x];
     17     }
     18 }a[5555];
     19 int sta[5555], top;
     20  
     21 int gcd(int x, int y)
     22 {
     23     return y ? gcd(y, x % y) : x;
     24 }
     25  
     26 void push_up(int k)
     27 {
     28     int c0 = a[k][0], c1 = a[k][1];
     29     a[k].mn = a[k].val ? k : 0, a[k].mx = k;
     30     if(a[a[c0].mn].val && a[a[c0].mn].val < a[a[k].mn].val)
     31         a[k].mn = a[c0].mn;
     32     if(a[a[c0].mx].val > a[a[k].mx].val) a[k].mx = a[c0].mx;
     33     if(a[a[c1].mn].val && a[a[c1].mn].val < a[a[k].mn].val)
     34         a[k].mn = a[c1].mn;
     35     if(a[a[c1].mx].val > a[a[k].mx].val) a[k].mx = a[c1].mx;
     36 }
     37  
     38 void push_down(int k)
     39 {
     40     if(a[k].rev)
     41     {
     42         swap(a[k][0], a[k][1]), a[k].rev = 0;
     43         a[a[k][0]].rev ^= 1, a[a[k][1]].rev ^= 1;
     44     }
     45 }
     46  
     47 bool isroot(int x)
     48 {
     49     return a[a[x].fa][0] != x && a[a[x].fa][1] != x;
     50 }
     51  
     52 void rotate(int x)
     53 {
     54     int y = a[x].fa, z = a[y].fa, dy = a[y][1] == x;
     55     if(!isroot(y)) a[z][a[z][1] == y] = x;
     56     a[y][dy] = a[x][!dy], a[a[x][!dy]].fa = y;
     57     a[x][!dy] = y, a[y].fa = x, a[x].fa = z;
     58     push_up(y);
     59 }
     60  
     61 void splay(int x)
     62 {
     63     sta[top = 1] = x;
     64     for(int i = x; !isroot(i); i = a[i].fa)
     65         sta[++top] = a[i].fa;
     66     while(top)
     67         push_down(sta[top--]);
     68     while(!isroot(x))
     69     {
     70         int y = a[x].fa, z = a[y].fa;
     71         if(!isroot(y))
     72             if(a[y][1] == x ^ a[z][1] == y) rotate(x);
     73             else rotate(y);
     74         rotate(x);
     75     }
     76     push_up(x);
     77 }
     78  
     79 void access(int x)
     80 {
     81     for(int i = 0; x; x = a[x].fa)
     82         splay(x), a[x][1] = i, i = x;
     83 }
     84  
     85 void make_root(int x)
     86 {
     87     access(x), splay(x), a[x].rev ^= 1;
     88 }
     89  
     90 void link(int x, int y)
     91 {
     92     make_root(x), a[x].fa = y;
     93 }
     94  
     95 void cut(int x, int y)
     96 {
     97     make_root(x), access(y), splay(y);
     98     a[y][0] = a[x].fa = 0, push_up(y);
     99 }
    100  
    101 int find_root(int x)
    102 {
    103     access(x), splay(x);
    104     while(a[x][0])
    105         x = a[x][0];
    106     return x;
    107 }
    108  
    109 int main()
    110 {
    111     int n, m, u, v, sss, ttt, mx = 50000, mn = 1, tmp;
    112     scanf("%d%d", &n, &m);
    113     for(int i = 1; i <= m; ++i)
    114     {
    115         scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
    116         if(e[i].u == e[i].v) --i, --m;
    117     }
    118     scanf("%d%d", &sss, &ttt);
    119     sss += m, ttt += m;
    120     sort(e + 1, e + m + 1);
    121     a[0].val = 50000, a[0].mx = 5554;
    122     for(int i = 1; i <= m + n; ++i)
    123         a[i].val = e[i].w, push_up(i);
    124     for(int i = 1; i <= m; ++i)
    125     {
    126         u = e[i].u + m, v = e[i].v + m;
    127         if(find_root(u) == find_root(v))
    128         {
    129             make_root(u), access(v), splay(v);
    130             tmp = a[v].mn;
    131             cut(e[tmp].u + m, tmp);
    132             cut(e[tmp].v + m, tmp);
    133         }
    134         link(u, i), link(v, i);
    135         if(find_root(sss) != find_root(ttt)) continue;
    136         make_root(sss), access(ttt), splay(ttt);
    137         if(1.0 * a[a[ttt].mx].val / a[a[ttt].mn].val < 1.0 * mx / mn)
    138             mx = a[a[ttt].mx].val, mn = a[a[ttt].mn].val;
    139     }
    140     tmp = gcd(mx, mn), mx /= tmp, mn /= tmp;
    141     if(mx == 50000) puts("IMPOSSIBLE");
    142     else if(mn == 1) printf("%d
    ", mx);
    143     else printf("%d/%d
    ", mx, mn);
    144     return 0;
    145 }
    View Code
  • 相关阅读:
    使用c#生成Identicon图片
    C#实现对文件目录的实时监控
    .Net Core的一些个人总结
    天天写业务代码,如何成为「技术大牛」?
    两款工作流JBPM和CCBPM的对比
    MVC5+EasyUI+EF6+Linq通用权限系统出炉--登录(2)
    MVC5+EasyUI+EF6+Linq通用权限系统出炉(1)
    博客园,久违了
    VS2010 代码行数统计以及代码复制汉字出现乱码
    关于TerrainExplorer的一些资源网址设置方法(备忘)
  • 原文地址:https://www.cnblogs.com/CtrlCV/p/5667745.html
Copyright © 2020-2023  润新知