• [FJOI 2014]最短路径树问题


    Description

    给一个包含n个点,m条边的无向连通图。从顶点1出发,往其余所有点分别走一次并返回。
    往某一个点走时,选择总长度最短的路径走。若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径(如路径A为1,32,11,路径B为1,3,2,11,路径B字典序较小。注意是序列的字典序的最小,而非路径中节点编号相连的字符串字典序最小)。到达该点后按原路返回,然后往其他点走,直到所有点都走过。
    可以知道,经过的边会构成一棵最短路径树。请问,在这棵最短路径树上,最长的包含K个点的简单路径长度为多长?长度为该最长长度的不同路径有多少条?
    这里的简单路径是指:对于一个点最多只经过一次的路径。不同路径是指路径两端端点至少有一个不同,点A到点B的路径和点B到点A视为同一条路径。

    Input

    第一行输入三个正整数n,m,K,表示有n个点m条边,要求的路径需要经过K个点。接下来输入m行,每行三个正整数Ai,Bi,Ci(1<=Ai,Bi<=n,1<=Ci<=10000),表示Ai和Bi间有一条长度为Ci的边。数据保证输入的是连通的无向图。

    Output

    输出一行两个整数,以一个空格隔开,第一个整数表示包含K个点的路径最长为多长,第二个整数表示这样的不同的最长路径有多少条。

    Sample Input

    6 6 4
    1 2 1
    2 3 1
    3 4 1
    2 5 1
    3 6 1
    5 6 1

    Sample Output

    3 4

    HINT

    对于所有数据n<=30000,m<=60000,2<=K<=n。
    数据保证最短路径树上至少存在一条长度为K的路径。

    题解

    看错题意,以为是求包含 $k$ 个点的简单路径共多少条。调了好久...

    首先求字典序最小的最短路树,考虑将边拆成两条单向边,然后按终点从大到小排序,按序插入链式前向星中,保证找到的第一条最短路就是字典序最小的。

    点分就比较裸了,记深度为 $i$ 时最大的路径长度为 $sum_i$ ,长度为 $sum_i$ ,且深度为 $i$ 的路径数为 $cnt_i$ 直接转移就好了。

      1 //It is made by Awson on 2018.1.4
      2 #include <set>
      3 #include <map>
      4 #include <cmath>
      5 #include <ctime>
      6 #include <queue>
      7 #include <stack>
      8 #include <cstdio>
      9 #include <string>
     10 #include <vector>
     11 #include <cstdlib>
     12 #include <cstring>
     13 #include <iostream>
     14 #include <algorithm>
     15 #define LL long long
     16 #define LD long double
     17 #define Max(a, b) ((a) > (b) ? (a) : (b))
     18 #define Min(a, b) ((a) < (b) ? (a) : (b))
     19 using namespace std;
     20 const int N = 30000;
     21 const int M = 60000;
     22 const int INF = ~0u>>1;
     23  
     24 int n, m, k;
     25 struct tt {
     26     int to, next, cost;
     27 }edge[(N<<1)+5];
     28 int path[N+5], top, ans1, ans2;
     29 void add(int u, int v, int c) {
     30     edge[++top].to = v;
     31     edge[top].next = path[u];
     32     edge[top].cost = c;
     33     path[u] = top;
     34 }
     35  
     36 namespace SPFA {
     37     int a, b, c;
     38     struct ss {
     39     int u, v, c;
     40     ss() {}
     41     ss(int _u, int _v, int _c) {
     42         u = _u, v = _v, c = _c;
     43     }
     44     bool operator < (const ss &b) const {
     45         return v > b.v;
     46     }
     47     }w[(M<<1)+5];
     48     struct sss {
     49     int to, next, cost;
     50     }edge[(M<<1)+5];
     51     int path[N+5], top, tot, pre[N+5], vis[N+5], prec[N+5];
     52     LL dist[N+5];
     53     void add_e(int u, int v, int c) {
     54     edge[++top].to = v;
     55     edge[top].cost = c;
     56     edge[top].next = path[u];
     57     path[u] = top;
     58     }
     59     void spfa() {
     60     memset(dist, 127/3, sizeof(dist)); dist[1] = 0;
     61     queue<int>Q; Q.push(1); vis[1] = 1;
     62     while (!Q.empty()) {
     63         int u = Q.front(); Q.pop(); vis[u] = 0;
     64         for (int i = path[u]; i; i = edge[i].next)
     65         if (dist[edge[i].to] > dist[u]+edge[i].cost) {
     66             dist[edge[i].to] = dist[u]+edge[i].cost;
     67             pre[edge[i].to] = u, prec[edge[i].to] = edge[i].cost;
     68             if (!vis[edge[i].to]) {
     69             vis[edge[i].to] = 1; Q.push(edge[i].to);
     70             }
     71         }
     72     }
     73     }
     74     void main() {
     75     scanf("%d%d%d", &n, &m, &k);
     76     for (int i = 1; i <= m; i++) {
     77         scanf("%d%d%d", &a, &b, &c);
     78         w[++tot] = ss(a, b, c), w[++tot] = ss(b, a, c);
     79     }
     80     sort(w+1, w+1+tot);
     81     for (int i = 1; i <= tot; i++) add_e(w[i].u, w[i].v, w[i].c);
     82     spfa();
     83     for (int i = 2; i <= n; i++) add(pre[i], i, prec[i]), add(i, pre[i], prec[i]);
     84     }
     85 }
     86 namespace Point_divide {
     87     int size[N+5], mx[N+5], minsize, root, vis[N+5], cnt[N+5], sum[N+5];
     88     void get_size(int o, int fa) {
     89     size[o] = 1, mx[o] = 0;
     90     for (int i = path[o]; i; i = edge[i].next)
     91         if (edge[i].to != fa && !vis[edge[i].to]) {
     92         get_size(edge[i].to, o);
     93         size[o] += size[edge[i].to];
     94         mx[o] = Max(mx[o], size[edge[i].to]);
     95         }
     96     }
     97     void get_root(int o, int rt, int fa) {
     98     mx[o] = Max(mx[o], size[rt]-size[o]);
     99     if (mx[o] < minsize) minsize = mx[o], root = o;
    100     for (int i = path[o]; i; i = edge[i].next)
    101         if (edge[i].to != fa && !vis[edge[i].to]) get_root(edge[i].to, rt, o);
    102     }
    103     void get_ans(int o, int fa, int dep, int cost) {
    104     if (dep >= k) return;
    105     if (cnt[k-1-dep] && ans1 < cost+sum[k-1-dep]) ans1 = cost+sum[k-1-dep], ans2 = cnt[k-1-dep];
    106     else if (cnt[k-1-dep] && ans1 == cost+sum[k-1-dep]) ans2 += cnt[k-1-dep];
    107     for (int i = path[o]; i; i = edge[i].next)
    108         if (edge[i].to != fa && !vis[edge[i].to]) get_ans(edge[i].to, o, dep+1, cost+edge[i].cost);
    109     }
    110     void get_update(int o, int fa, int dep, int cost) {
    111     if (dep >= k) return;
    112     if (sum[dep] < cost) sum[dep] = cost, cnt[dep] = 1;
    113     else if (sum[dep] == cost) ++cnt[dep];
    114     for (int i = path[o]; i; i = edge[i].next)
    115         if (edge[i].to != fa && !vis[edge[i].to]) get_update(edge[i].to, o, dep+1, cost+edge[i].cost);
    116     }
    117     void get_clean(int o, int fa, int dep) {
    118     if (dep >= k) return;
    119     cnt[dep] = 0, sum[dep] = 0;
    120     for (int i = path[o]; i; i = edge[i].next)
    121         if (edge[i].to != fa && !vis[edge[i].to]) get_clean(edge[i].to, o, dep+1);
    122     }
    123     void work(int o) {
    124     minsize = INF;
    125     get_size(o, 0), get_root(o, o, 0);
    126     vis[root] = 1; cnt[0] = 1;
    127     for (int i = path[root]; i; i = edge[i].next)
    128         if (!vis[edge[i].to]) get_ans(edge[i].to, root, 1, edge[i].cost), get_update(edge[i].to, root, 1, edge[i].cost);
    129     cnt[0] = 0;
    130     for (int i = path[root]; i; i = edge[i].next)
    131         if (!vis[edge[i].to]) get_clean(edge[i].to, root, 1);
    132     for (int i = path[root]; i; i = edge[i].next)
    133         if (!vis[edge[i].to]) work(edge[i].to);
    134     }
    135     void main() {work(1); }
    136 }
    137 void work() {
    138     SPFA::main();
    139     Point_divide::main();
    140     printf("%d %d
    ", ans1, ans2);
    141 }
    142 int main() {
    143     work();
    144     return 0;
    145 }
  • 相关阅读:
    个税
    MC9S08中断机制
    各大公司面试笔试
    uc/OSII 任务切换
    Source Insight 使用
    充70送70
    兔年大年30
    pip更新后报No module named pip
    MsSql Md5
    iOS UIImage扩展方法(category):放大、旋转、合并UIImage、增加渐变层、添加阴影、调节透明度、保存到相册
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/8202372.html
Copyright © 2020-2023  润新知