洛谷P1967 [Noip2013]货车运输

题目描述

A 国有 n 座城市,编号从1 n ,城市之间有m条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

输入输出格式

输入格式

  • 输入文件名为truck.in

    • 输入文件第一行有两个用一个空格隔开的整数 n,m ,表示 A 国有 n 座城市和m条道路。 接下来 m 行每行3个整数 x,y,z ,每两个整数之间用一个空格隔开,表示从 x 号城市到y号城市有一条限重为 z 的道路。注意: x不等于 y ,两座城市之间可能有多条道路
    • 接下来一行有一个整数q,表示有 q 辆货车需要运货。
    • 接下来q行,每行两个整数 x,y ,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到y城市,注意: x 不等于y
    • 输出格式
      • 输出文件名为 truck.out
      • 输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出1

      输入输出样例

      输入样例#1
      • 4 3
        1 2 4
        2 3 3
        3 1 1
        3
        1 3
        1 4
        1 3
      输出样例#1
      • 3
        -1
        3

      说明

      • 对于30%的数据 0<n<1,0000<m<10,0000<q<1,000
      • 对于60%的数据 0<n<1,0000<m<50,0000<q<1,000
      • 对于100%的数据 0<n<10,0000<m<50,0000<q<30,0000z100,000

      原题地址

      分析 最大生成树森林 + 树链剖分

      • 题目的大意就是给出无向图中的 q 对点,要求找出连接每对点的一种路径方案,使得每条路径上边权的最小值最大,并输出这个最小值。
      • 因为这是无向图,于是我们做一遍最大生成树,这样每对点的答案就为生成树路径上边权的最小值。
      • 简单用反证法证明一下:
      • 假设两点间存在一条边不在生成树上,并且边权大于它们生成树路径上边权的最小值,那么按照Kruskal算法的贪心思想,我们肯定会根据边权优先选择这条边作为生成树上的边而不选择边权为最小值的那条边,这与之前的假设矛盾,因此我们得到两点的生成树路径上每条边的边权肯定都是尽量大的。
      • 于是问题就被转化:给出一棵树,求树上两点路径上边权的最小值。
      • 这可以用倍增 LCA 算,但我写的是树链剖分(感觉好像没什么人用)。
      • 主要注意下这张图可能是不连通的,因此实际上应该是最大生成树森林,则不在同一棵树上即输出 1 ,这可以直接用并查集判断;并且我们要对森林中的每一棵树都剖分一遍(记一个 bool 数组表示点是否被访问过,然后每找到一个没被访问过的点就以这个点为根遍历出一棵树)。
      • 另外剖分的是边权而不是点权,所以我们进行转化,把边权的信息存储在所连两点中深度较大的那一个点上,然后在询问时不查询深度最小的点即可。

      代码

      (有点小长……)

      #include <iostream>
      #include <cstdio>
      #include <algorithm>
      #define sL (s << 1)
      #define sR (s << 1 | 1) 
      
      using namespace std;
      const int Maxn = 0x3f3f3f3f;
      const int N = 1e4 + 5, M = 1e5 + 5; 
      int dep[N], sze[N], fa[N], idx[N], top[N], son[N], pos[N];
      int sol[N], val[M], f[N]; bool vis[N];
      int n, m, Q, P; 
      
      char frd[M], *hed = frd + M;
      char fwt[M << 3], *opt = fwt;
      const char *tal = hed;
      
      inline char nxtChar()
      {
          if (hed == tal)
           fread(frd, 1, M, stdin), hed = frd;
          return *hed++;
      }
      inline int get()
      {
          char ch; int res = 0;
          while ((ch = nxtChar()) < '0' || ch > '9');
          res = ch ^ 48;
          while ((ch = nxtChar()) >= '0' && ch <= '9')
           res = (res << 3) + (res << 1) + (ch ^ 48);
          return res;
      }
      
      inline void put(int x)
      {
          if (x > 9) put(x / 10);
          *opt++ = x % 10 + 48;
      }
      
      struct point {int l, r, w;}a[M];
      struct Edge {int to, cst; Edge *nxt;}p[M], *T = p, *lst[N];
      
      inline bool cmp(const point &x, const point &y) {return x.w > y.w;}
      
      inline void AddEdge(const int &x, const int &y, const int &z)
      {
          (++T)->nxt = lst[x]; lst[x] = T; T->to = y; T->cst = z;
          (++T)->nxt = lst[y]; lst[y] = T; T->to = x; T->cst = z;
      }
      
      inline int Find(const int &x)
      {
          if (f[x] != x) f[x] = Find(f[x]);
          return f[x];
      }
      
      inline void Dfs1(const int &x, const int &F)
      {
          dep[x] = dep[F] + 1; fa[x] = F; 
          sze[x] = 1; vis[x] = true;
          for (Edge *e = lst[x]; e; e = e->nxt)
          {
              int y = e->to;
              if (y == F) continue; sol[y] = e->cst;
              Dfs1(y, x); sze[x] += sze[y];
              if (sze[y] > sze[son[x]]) son[x] = y;
          }
      }
      
      inline void Dfs2(const int &x)
      {
          int y;
          if (son[x])
          {
              top[y = son[x]] = top[x];
              idx[pos[y] = ++P] = y;
              Dfs2(y);
          }
          for (Edge *e = lst[x]; e; e = e->nxt)
           if (!top[y = e->to])
           {
              top[y] = y; 
              idx[pos[y] = ++P] = y;
              Dfs2(y);  
           }
      }
      
      inline int Min(const int &x, const int &y) {return x < y ? x : y;}
      inline void Push(const int &s) {val[s] = Min(val[sL], val[sR]);}
      
      inline void Build(const int &s, const int &l, const int &r)
      {
          if (l == r) return (void)(val[s] = sol[idx[l]]);
          int mid = l + r >> 1;
          Build(sL, l, mid); Build(sR, mid + 1, r);
          Push(s);
      }
      
      inline int Query(const int &s, const int &l, const int &r, const int &x, const int &y)
      {
          if (l == x && r == y) return val[s];
          int mid = l + r >> 1;
          if (y <= mid) return Query(sL, l, mid, x, y);
           else if (x > mid) return Query(sR, mid + 1, r, x, y);
            else return Min(Query(sL, l, mid, x, mid),
                            Query(sR, mid + 1, r, mid + 1, y));
      }
      
      inline void Swap(int &x, int &y) {x ^= y; y ^= x; x ^= y;}
      inline void CkMin(int &x, const int &y) {if (x > y) x = y;}
      
      inline int PaQuery(int x, int y)
      {
          int res = Maxn;
          while (top[x] != top[y])
          {
              if (dep[top[x]] < dep[top[y]]) Swap(x, y);
              CkMin(res, Query(1, 1, n, pos[top[x]], pos[x]));
              x = fa[top[x]];
          }
          if (x == y) return res;
          if (dep[x] < dep[y]) Swap(x, y);
          return Min(res, Query(1, 1, n, pos[y] + 1, pos[x]));
      }
      
      int main()
      {
          n = get(); m = get();
          for (int i = 1; i <= m; ++i)
          {
              a[i].l = get(); a[i].r = get(); a[i].w = get();
          }
          for (int i = 1; i <= n; ++i) f[i] = i;
          sort(a + 1, a + m + 1, cmp); int K = 0;
          for (int i = 1; i <= m; ++i)
          {
              int tx = Find(a[i].l), 
                  ty = Find(a[i].r);
              if (tx != ty)
              {
                  f[tx] = ty; K++;
                  AddEdge(a[i].l, a[i].r, a[i].w);
              }
              if (K == n - 1) break;
          }
          Q = get(); int x, y;
          for (int i = 1; i <= n; ++i)
           if (!vis[i]) 
           {
              Dfs1(i, 0);
              idx[pos[i] = ++P] = top[i] = i; 
              Dfs2(i);
           }
          Build(1, 1, n);
          for (int i = 1; i <= Q; ++i)
          {
              x = get(); y = get();
              if (Find(x) != Find(y)) *opt++ = '-', *opt++ = '1';
               else put(PaQuery(x, y));
              *opt++ = '\n';  
          }
          fwrite(fwt, 1, opt - fwt, stdout);
      }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值