NOIP 2013 火车运输【Kruskal + 树链剖分】

NOIP 2013 火车运输【树链剖分】

树链剖分

题目描述 Description

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

输入描述 Input Description

第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。
接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意:x 不等于 y,两座城市之间可能有多条道路。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意:x 不等于 y。

输出描述 Output Description

输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。

样例输入 Sample Input

4 3 
1 2 4 
2 3 3 
3 1 1 
3
1 3 
1 4 
1 3

样例输出 Sample Output

3
-1
3

数据范围及提示 Data Size & Hint

对于 30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q < 1,000; 
对于 60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q < 1,000; 
对于 100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q < 30,000,0 ≤ z ≤ 100,000。

—————————————————————分割线—————————————————————

 分析:

货车要运输尽可能多的货物,一定要保证两点间的路径上载重尽可能大,即,两点间的最大载重路径一定在最大生成树上,否则,与最大生成树的定义相悖。

只需要求解,在最大生成树上的两点间路径上的最大载重,这里使用树链剖分解决。

[ATTENTION]:这里注意点权和边权的处理,E_val[]表示该节点与其父节点连边的边权。根节点的E_val[]设置为 INF,可以视为加入一个虚拟节点(如图) ,再用线段树维护即可。

注释&代码:

Code By SHHHS

  1 #include "bits/stdc++.h"
  2
  3 using namespace std ;
  4
  5 struct MST{int x , y , val ; } ;
  6 struct Edge{int to , next , val ; } ;
  7 struct SegTree{int l , r , mintr ; } ;
  8 const int INF = 2147483647 ;
  9 const int maxN = 300010 ;
 10 typedef long long QAQ ;
 11
 12 MST MST_e[ maxN ] ;
 13 Edge e [ maxN ] ;
 14 SegTree tr[ maxN << 2 ] ;
 15 int head[maxN] , father[maxN], DFN[maxN], hv[maxN],rank[maxN], E_val[maxN], start[maxN], pre[maxN];
 16 bool vis[maxN] ;
 17
 18 int cnt , dfs_num ;
 19 QAQ Ans = INF ;
 20
 21 void Init ( int n ){for ( int i = 1 ; i <= n ; ++i )father[ i ] = i ; }
 22 int getfa ( int x ){if(father[x] == x)return x ; else return father[ x ] = getfa( father[ x ] ) ; }
 23 inline bool cmp ( MST x , MST y ){ return x.val > y.val ; }
 24 inline int gmin ( int x , int y ){ return x > y ? y : x ; }
 25 inline void Union_Set ( const int x , const int y ){ father[ x ] = y ; }
 26 inline void Push_up ( int i ){tr[ i ].mintr = gmin (tr[ i << 1 | 1 ].mintr, tr[ i << 1 ].mintr) ; }
 27 inline void gswap ( int &x , int &y ) {int temp = x ; x = y ; y = temp ; }
 28
 29 void Build_Tree ( int x , int y , int i ) {//建树
 30         tr[ i ].l = x ;
 31         tr[ i ].r = y ;
 32         if ( x == y ) tr[ i ].mintr = E_val[ rank[ x ] ] ;
 33         else {
 34                 int mid = ( tr[ i ].l + tr[ i ].r ) >> 1 ;
 35                 Build_Tree ( x , mid , i << 1 ) ;
 36                 Build_Tree ( mid + 1 , y , i << 1 | 1 ) ;
 37                 Push_up ( i ) ;
 38         }
 39 }
 40
 41 inline void Add_Edge ( const int x , const int y , const int _val ) {//建边
 42         e[ ++cnt ].to = y ;
 43         e[ cnt ].val = _val ;
 44         e[ cnt ].next = head[ x ] ;
 45         head[ x ] = cnt ;
 46 }
 47
 48 void MST ( int N , int M ) {//最大生成树
 49         int cnt_ = 0 ;
 50         Init ( N ) ;
 51         sort ( MST_e + 1 , MST_e + M + 1 , cmp ) ;//排序
 52         for ( int i = 1 ; i <= M ; ++i ) {
 53                 int px = getfa ( MST_e[i].x ) ;//祖先
 54                 int py = getfa ( MST_e[i].y ) ;//
 55                 if ( px == py ) continue ;
 56                 else {
 57                         Union_Set ( px , py ) ;
 58                         Add_Edge ( MST_e[i].x , MST_e[i].y , MST_e[i].val ) ;//建边
 59                         Add_Edge ( MST_e[i].y , MST_e[i].x , MST_e[i].val ) ;
 60                         ++cnt_ ;
 61                 }
 62                 if ( cnt_ == N - 1 ) break ;
 63         }
 64 }
 65
 66 int Init_DFS ( const int x , const int fa ) {
 67         vis[ x ] = true ;
 68         int cnt_ = 1 , max_ = 0 ;
 69         for ( int i = head[ x ] ; i ; i = e[ i ].next ) {
 70                 int temp = e[ i ].to ;
 71                 if ( temp == fa ) continue ;
 72                 int _ = Init_DFS ( temp , x ) ;
 73                 if ( _ > max_ ) {
 74                         max_ = _ ;
 75                         hv[ x ] = temp ;
 76                 }
 77                 cnt_ += _;
 78         }
 79         return cnt_ ;
 80 }
 81
 82 void DFS ( const int x , const int fa ) {
 83         vis [ x ] = false ;
 84         if ( !start[ x ] ) start[ x ] = start[ fa ] ;
 85         DFN[ x ] = ++ dfs_num ;
 86         rank[ dfs_num ] = x ;
 87         if ( hv[ x ] ) DFS ( hv[ x ] , x ) ;
 88         for ( int i = head[ x ] ; i ; i = e[ i ].next ) {
 89                 if ( e[ i ].to == fa ) continue ;
 90                 E_val[ e[ i ].to ] = e[ i ] .val ;
 91                 if ( e[ i ].to != hv[ x ] && e[ i ].to != fa && vis[ e[ i ].to ] == true ) {
 92                         int temp = e[ i ].to ;
 93                         start[ temp ] = temp ;
 94                         pre [ temp ] = x ;
 95                         DFS ( temp , x ) ;
 96                 }
 97         }
 98 }
 99
100 QAQ Query_Tree ( int q , int w , int i ) {//线段树查询
101         if ( q <= tr[i].l && w >= tr[i].r ) return tr[i].mintr;
102         else {
103                 int mid = ( tr[ i ].l + tr[ i ].r ) >> 1 ;
104                 if ( q > mid ) return Query_Tree ( q , w , i << 1 | 1) ;
105                 else if ( w <= mid ) return Query_Tree ( q , w , i << 1 ) ;
106                 else return gmin ( Query_Tree ( q , w , i << 1 | 1 ) , Query_Tree ( q , w , i << 1 ) ) ;
107         }
108 }
109
110 void Solve ( const int x , const int y ) {
111         int px = x , py = y ;
112         while ( start[ px ] != start[ py ] ) {//不在同一条链上
113                 if ( DFN[ start[ px ] ] > DFN[ start[ py ] ] ) {//px跳
114                         Ans = gmin ( Ans , Query_Tree ( DFN[start[ px ]] , DFN[px] , 1 ) ) ;
115                         px = pre[ start[ px ] ] ;
116                 }
117                 else {//py跳
118                         Ans = gmin ( Ans , Query_Tree ( DFN[start[ py ]] , DFN[py] , 1 ) ) ;
119                         py = pre[ start[ py ] ] ;
120                 }
121         }
122
123         if ( px == py ) return ;
124         int dfn_px = DFN[ px ] , dfn_py = DFN[ py ] ;
125         if ( dfn_px  > dfn_py ) gswap ( dfn_px , dfn_py ) ;//不加这句话会炸栈
126         Ans = gmin ( Ans , Query_Tree ( dfn_px + 1, dfn_py , 1 ) );
127 }
128
129 void DEBUG__ ( int n ){
130         putchar ( ‘\n‘ ) ;
131         for ( int i = 1 ; i <= n ; ++i )printf ("%d : %d %d\n", i , E_val[rank[ i ] ] , E_val[ i ]) ;
132         putchar ( ‘\n‘ ) ;
133 }
134 void DEBUG_ ( int m ) {
135         putchar(‘\n‘);
136         for ( int i = 1 ; i <= m ; ++i ) printf ("%d %d %d\n" , MST_e[ i ].x , MST_e[ i ].y , MST_e[ i ].val) ;
137 }
138 int main ( ) {
139         int N , M , Q , _x , _y ;
140         scanf ( "%d%d" , &N , &M ) ;
141         for ( int i = 1 ; i <= M ; ++i )//读入
142                 scanf ( "%d%d%d" , &MST_e[ i ].x , &MST_e[ i ].y , &MST_e[ i ].val ) ;
143         MST ( N , M ) ;//最大生成树
144         for ( int i = 1 ; i <= N ; ++i ) {//第一遍DFS
145                 if ( !vis[i] ) {
146                         Init_DFS ( i , i ) ;
147                         start[ i ] = i ;
148                         E_val[ i ] = INF ;
149                 }
150         }
151
152         //DEBUG_( M );
153         for ( int i = 1 ; i <= N ; ++i ) {//第二遍重儿子优先的DFS
154                 if ( vis[ i ] ) {
155                         DFS ( i , i ) ;
156                 }
157         }
158
159         Build_Tree ( 1 , dfs_num , 1 ) ;//建树
160
161         //DEBUG__( dfs_num ) ;
162         scanf ( "%d" , &Q ) ;
163         while ( Q-- ){
164                 Ans = INF ;//设置最大值
165                 scanf ( "%d%d" , &_x , &_y ) ;
166                 if ( getfa( _x ) == getfa( _y ) ) {//在一棵树上可相互到达
167                         Solve ( _x , _y ) ;
168                         printf ( "%lld\n" , Ans ) ;
169                 }
170                 else printf("-1\n");
171         }
172         return 0 ;
173 }

2016-10-02 19:52:23

()

时间: 2024-10-08 13:57:00

NOIP 2013 火车运输【Kruskal + 树链剖分】的相关文章

NOIP 2015 BZOJ 4326 运输计划 (树链剖分+二分)

题目大意:给一棵带有边权的树,有m条路径,可以使一条边的权值为0,求最远路径的最小值.题解:因为题目的数据点给的很明确 因此可以打n*n的去骗前五十分.另外m=1时可以特判另外打个程序骗60分.60分程序: #include<cstdio> #include<algorithm> int dep[100001],fa[100001],last[200001],next[200001],e[200001],val[200001],cost[100001],tot,a,b,vv,u[1

【BZOJ-4326】运输计划 树链剖分 + 树上差分 + 二分

4326: NOIP2015 运输计划 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 703  Solved: 461[Submit][Status][Discuss] Description 公元 2044 年,人类进入了宇宙纪元.L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球.小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui

P2680 运输计划 树链剖分

在一颗有边权的树上有m条路径,清零一条边的边权使得m条路径的最大值最小. 输出这个最大值 显然 要遍历这m条路的最长路(如果最长路有多条随意遍历一条即可) 因为树上距离不修改  那么用前缀和维护树上路径长度可以少一个log 然后遍历最长路的每一条边  ans=min(ans,max(最长路的长度-这条路的长度,不经过这条边的最长路长度) 所以现在需要维护的是不经过这条边的最长路长度 可以遍历m  将不在这条路径上的所有边 维护一个值(就是这条路径的长度) 用线段树维护最大值即可 #include

[HDU3710] Battle Over Cities [树链剖分+线段树+并查集+kruskal+思维]

题面 一句话题意: 给定一张 N 个点, M 条边的无向连通图, 每条边上有边权 w . 求删去任意一个点后的最小生成树的边权之和. 思路 首先肯定要$kruskal$一下 考虑$MST$里面去掉一个点,得到一堆联通块,我们要做的就是用原图中剩下的边把这些联通块穿起来 考虑这个点$u$在$MST$上的位置,可以知道有两种边:一种是从$u$的任意一个儿子的子树连到$u$的子树外面的,一种是在$u$的两个儿子的子树之间连接的 第一种情况: 考虑边$(u,v)$,没有进入$MST$中,那么若它是某个节

树链剖分2——边权改点权

实验对象——2013 noip day1 T3 本来可以直接用倍增lca解决..但是我比较的扯淡..所以用树链剖分来搞 和普通点权不同的是,对于一颗树来说,每一个点的点权被定义为他的父亲到他的边权,所以与一般的树链剖分相比,最后统一到一条链上时,线段树维护的一边端点要加1..其他的就没了.然后注意往上跳的时候的比较时dep[un[a]] 和 dep[un[b]] 不是dep[a] dep[b]; #include <iostream> #include <cstdio> #incl

ACdream 1103 瑶瑶正式成为CEO(树链剖分+费用流)

Problem Description 瑶瑶(tsyao)是某知名货运公司(顺丰)的老板,这个公司很大,货物运输量极大,因此公司修建了许多交通设施,掌控了一个国家的交通运输. 这个国家有n座城市,公司的总部在1号城市. 公司下管辖的有m条道路和n-1段火车线路. 这m条道路和n-1条火车线路都可以用u来表示起点,v来表示终点(数据保证m条道路和n-1条火车线路构成有向无环图). 这n-1段火车道保证从1号城市出发,能够有唯一的一道只包含火车道的线路到达其他n-1座城市. 每条道路和每段火车道都有

UVA 11354 - Bond (最小生成树 + 树链剖分)

题目链接~~> 做题感悟:这题开始看到时感觉不是树不好处理,一想可以用 Kruskal 处理成树 ,然后就好解决了. 解题思路: 先用 Kruskal 处理出最小生成树,然后用树链剖分 + 线段树处理就可以了. 代码: #include<iostream> #include<sstream> #include<map> #include<cmath> #include<fstream> #include<queue> #incl

BZOJ 4326 树链剖分+二分+差分+记忆化

去年NOIP的时候我还不会树链剖分! 还是被UOJ 的数据卡了一组. 差分的思想还是很神啊! 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <algorithm> 5 #include <ctime> 6 #include <cstdlib> 7 using namespace std; 8 const int Maxn=300100

P3398 仓鼠找sugar(树链剖分)

P3398 仓鼠找sugar 题目描述 小仓鼠的和他的基(mei)友(zi)sugar住在地下洞穴中,每个节点的编号为1~n.地下洞穴是一个树形结构.这一天小仓鼠打算从从他的卧室(a)到餐厅(b),而他的基友同时要从他的卧室(c)到图书馆(d).他们都会走最短路径.现在小仓鼠希望知道,有没有可能在某个地方,可以碰到他的基友? 小仓鼠那么弱,还要天天被zzq大爷虐,请你快来救救他吧! 输入输出格式 输入格式: 第一行两个正整数n和q,表示这棵树节点的个数和询问的个数. 接下来n-1行,每行两个正整