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。

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

 分析:

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

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

Gster大爷说这道题可以用树剖写,于是就尝试了树剖。

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

注释&代码:

Code By SHHHS

解法一:

 /*
树链剖分
author : SHHHS
2016-10-02 09:04:15
*/
#include "bits/stdc++.h" using namespace std ; struct MST{int x , y , val ; } ;
struct Edge{int to , next , val ; } ;
struct SegTree{int l , r , mintr ; } ;
const int INF = ;
const int maxN = ;
typedef long long QAQ ; MST MST_e[ maxN ] ;
Edge e [ maxN ] ;
SegTree tr[ maxN << ] ;
int head[maxN] , father[maxN], DFN[maxN], hv[maxN],rank[maxN], E_val[maxN], start[maxN], pre[maxN];
bool vis[maxN] ; int cnt , dfs_num ;
QAQ Ans = INF ; void Init ( int n ){for ( int i = ; i <= n ; ++i )father[ i ] = i ; }
int getfa ( int x ){if(father[x] == x)return x ; else return father[ x ] = getfa( father[ x ] ) ; }
inline bool cmp ( MST x , MST y ){ return x.val > y.val ; }
inline int gmin ( int x , int y ){ return x > y ? y : x ; }
inline void Union_Set ( const int x , const int y ){ father[ x ] = y ; }
inline void Push_up ( int i ){tr[ i ].mintr = gmin (tr[ i << | ].mintr, tr[ i << ].mintr) ; }
inline void gswap ( int &x , int &y ) {int temp = x ; x = y ; y = temp ; }
bool Check ( const int x , const int y ) { return getfa( x ) == getfa( y ) ? true : false ; } void Build_Tree ( int x , int y , int i ) {//建树
tr[ i ].l = x ;
tr[ i ].r = y ;
if ( x == y ) tr[ i ].mintr = E_val[ rank[ x ] ] ;
else {
int mid = ( tr[ i ].l + tr[ i ].r ) >> ;
Build_Tree ( x , mid , i << ) ;
Build_Tree ( mid + , y , i << | ) ;
Push_up ( i ) ;
}
} inline void Add_Edge ( const int x , const int y , const int _val ) {//建边
e[ ++cnt ].to = y ;
e[ cnt ].val = _val ;
e[ cnt ].next = head[ x ] ;
head[ x ] = cnt ;
} void MST ( int N , int M ) {//最大生成树
int cnt_ = ;
Init ( N ) ;
sort ( MST_e + , MST_e + M + , cmp ) ;//排序
for ( int i = ; i <= M ; ++i ) {
int px = getfa ( MST_e[i].x ) ;//祖先
int py = getfa ( MST_e[i].y ) ;//
if ( px == py ) continue ;
else {
Union_Set ( px , py ) ;
Add_Edge ( MST_e[i].x , MST_e[i].y , MST_e[i].val ) ;//建边
Add_Edge ( MST_e[i].y , MST_e[i].x , MST_e[i].val ) ;
++cnt_ ;
}
if ( cnt_ == N - ) break ;
}
} int Init_DFS ( const int x , const int fa ) {
vis[ x ] = true ;
int cnt_ = , max_ = ;//cnt_儿子数
for ( int i = head[ x ] ; i ; i = e[ i ].next ) {
int temp = e[ i ].to ;//下一个节点
if ( temp == fa ) continue ;
int _ = Init_DFS ( temp , x ) ;//启发式建链
if ( _ > max_ ) {
max_ = _ ;
hv[ x ] = temp ;
}
cnt_ += _;
}
return cnt_ ;
} void DFS ( const int x , const int fa ) {
vis [ x ] = false ;
if ( !start[ x ] ) start[ x ] = start[ fa ] ;//链头
DFN[ x ] = ++ dfs_num ;// DFS序
rank[ dfs_num ] = x ;// 节点在DFN数组中的位置
if ( hv[ x ] ) DFS ( hv[ x ] , x ) ;//重儿子优先DFS
for ( int i = head[ x ] ; i ; i = e[ i ].next ) {
if ( e[ i ].to == fa ) continue ;
E_val[ e[ i ].to ] = e[ i ] .val ;
if ( e[ i ].to != hv[ x ] && e[ i ].to != fa && vis[ e[ i ].to ] == true ) {
int temp = e[ i ].to ;
start[ temp ] = temp ;
pre [ temp ] = x ;
DFS ( temp , x ) ;
}
}
} QAQ Query_Tree ( int q , int w , int i ) {//线段树查询
if ( q <= tr[i].l && w >= tr[i].r ) return tr[i].mintr;
else {
int mid = ( tr[ i ].l + tr[ i ].r ) >> ;
if ( q > mid ) return Query_Tree ( q , w , i << | ) ;
else if ( w <= mid ) return Query_Tree ( q , w , i << ) ;
else return gmin ( Query_Tree ( q , w , i << | ) , Query_Tree ( q , w , i << ) ) ;
}
} void Solve ( const int x , const int y ) {
int px = x , py = y ;
while ( start[ px ] != start[ py ] ) {//不在同一条链上
if ( DFN[ start[ px ] ] > DFN[ start[ py ] ] ) {//px跳
Ans = gmin ( Ans , Query_Tree ( DFN[start[ px ]] , DFN[px] , ) ) ;
px = pre[ start[ px ] ] ;
}
else {//py跳
Ans = gmin ( Ans , Query_Tree ( DFN[start[ py ]] , DFN[py] , ) ) ;
py = pre[ start[ py ] ] ;
}
} if ( px == py ) return ;
int dfn_px = DFN[ px ] , dfn_py = DFN[ py ] ;
if ( dfn_px > dfn_py ) gswap ( dfn_px , dfn_py ) ;//不加这句话会炸栈
Ans = gmin ( Ans , Query_Tree ( dfn_px + , dfn_py , ) );
} void DEBUG__ ( int n ){
putchar ( '\n' ) ;
for ( int i = ; i <= n ; ++i )printf ("%d : %d %d\n", i , E_val[rank[ i ] ] , E_val[ i ]) ;
putchar ( '\n' ) ;
}
void DEBUG_ ( int m ) {
putchar('\n');
for ( int i = ; i <= m ; ++i ) printf ("%d %d %d\n" , MST_e[ i ].x , MST_e[ i ].y , MST_e[ i ].val) ;
}
int main ( ) {
int N , M , Q , _x , _y ;
scanf ( "%d%d" , &N , &M ) ;
for ( int i = ; i <= M ; ++i )//读入
scanf ( "%d%d%d" , &MST_e[ i ].x , &MST_e[ i ].y , &MST_e[ i ].val ) ;
MST ( N , M ) ;//最大生成树
for ( int i = ; i <= N ; ++i ) {//第一遍DFS
if ( !vis[i] ) {
Init_DFS ( i , i ) ;
start[ i ] = i ;
E_val[ i ] = INF ;
}
} //DEBUG_( M );
for ( int i = ; i <= N ; ++i ) {//第二遍重儿子优先的DFS
if ( vis[ i ] ) {
DFS ( i , i ) ;
}
} Build_Tree ( , dfs_num , ) ;//建树 //DEBUG__( dfs_num ) ;
scanf ( "%d" , &Q ) ;
while ( Q-- ){
Ans = INF ;//设置最大值
scanf ( "%d%d" , &_x , &_y ) ;
if ( Check ( _x , _y ) ) {//在一棵树上可相互到达
Solve ( _x , _y ) ;
printf ( "%lld\n" , Ans ) ;
}
else printf("-1\n");
}
return ;
}

解法二:

(béi)增算法

 /*
树上倍增
Code By SHHHS
2016-10-04 12:20:10
*/
#include "bits/stdc++.h" using namespace std;
struct MST { int x,y,val;};
struct Edge{int to,next,val;};
const int INF = ;
const int maxN = ; MST MST_e[ maxN ] ;
Edge e[ maxN ] ;
int father[maxN],deep[maxN],fa[maxN][],g[maxN][],head[maxN],cnt;
bool vis[maxN]; inline bool cmp ( MST x , MST y ) { return x.val > y.val ; }
inline void Init ( int n ) { for ( int i= ; i<=n ; ++i ) father[ i ] = i ;}
int getfa( int x ){ return father[ x ] == x ? x : father[ x ] = getfa( father[ x ] ) ; }
inline void Union_Set ( const int x , const int y ){ father[ x ] = y ; }
inline int gmin ( int x , int y ) { return x > y ? y : x ;}
inline void gswap ( int &x , int &y ) { int temp = x ; x = y ; y = temp ; } void DFS ( int x , int pre ) {
vis[ x ] = true ;
for ( int i= ; i<= ; ++i ) {
if ( deep[ x ] < ( << i ) ) break ;
fa[ x ][ i ] = fa[ fa[ x ][ i - ] ][ i - ] ;
g[ x ][ i ] = gmin ( g[ x ][ i - ] , g[ fa[ x ][ i - ] ][ i - ] ) ;
}
for ( int i=head[ x ] ; i ; i=e[ i ].next ){
int temp = e[ i ].to ;
if( vis[ temp ] ||temp == pre ) continue;
fa[ temp ][ ] = x ;g[ temp ][ ] = e[ i ].val ;
deep[ temp ] = deep[ x ] + ;
DFS ( temp , x ) ;
}
} void Add_Edge ( const int x , const int y , const int _val ) {
e[ ++cnt ].to = y ;
e[ cnt ].val = _val ;
e[ cnt ].next = head[ x ] ;
head[ x ] = cnt ;
} int LCA ( int x , int y ) {
if ( deep[ x ] < deep[ y ] )gswap( x , y ) ;
int t = deep[ x ] - deep[ y ] ;
for ( int i= ; i<= ; ++i ) if( ( << i ) & t ) x = fa[ x ][ i ] ;
if( x == y ) return x ;
for ( int i= ; i>= ; --i ) {
if ( fa[ x ][ i ] == fa[ y ][ i ] ) continue ;
x = fa[ x ][ i ] ; y = fa[ y ][ i ] ;
}
return fa[ x ][ ] ;
} int solve ( int x , int y ) {
int ans = INF ;
if ( deep[ x ] < deep[ y ] )gswap( x , y ) ;
int t = deep[ x ] - deep[ y ] ;
for ( int i= ; i<= ; ++i ) if( ( << i ) & t )ans = gmin( ans , g[ x ][ i ] ) ,x = fa[ x ][ i ] ;
if ( x == y ) return ans ;
for ( int i= ; i>= ; --i ) {
if ( fa[ x ][ i ] == fa[ y ][ i ] ) continue ;
ans = gmin ( ans , gmin ( g[ x ][ i ] , g[ y ][ i ] ) ) ;
x = fa[ x ][ i ];
y = fa[ y ][ i ] ;
}
return gmin ( ans ,gmin ( g[ x ][ ] ,g[ y ][ ] ) ) ;
} void Kruskal ( int N , int M ) {
int cnt_ = ;
Init ( N ) ;
sort ( MST_e + , MST_e + M + , cmp ) ;
for ( int i = ; i <= M ; ++i ) {
int px = getfa ( MST_e[i].x ) ;
int py = getfa ( MST_e[i].y ) ;
if ( px == py ) continue ;
else {
Union_Set ( px , py ) ;
Add_Edge ( MST_e[i].x , MST_e[i].y , MST_e[i].val ) ;
Add_Edge ( MST_e[i].y , MST_e[i].x , MST_e[i].val ) ;
++cnt_ ;
}
if ( cnt_ == N - ) break ;
}
} int main ( ) {
memset ( g , / , sizeof( g ) ) ;
int N , M , x , y , Q ;
scanf ( "%d%d" , &N , &M ) ;
for ( int i= ; i<=M ; i++ )
scanf( "%d%d%d" , &MST_e[ i ].x , &MST_e[ i ].y , &MST_e[ i ].val ) ;
Kruskal ( N , M ) ;
for ( int i= ; i<=N ; ++i )
if( !vis[ i ] )
DFS ( i , i ) ;
scanf ( "%d" , &Q ) ;
while ( Q-- ) {
scanf( "%d%d" , &x , &y ) ;
if ( getfa( x ) != getfa( y ) )printf( "-1\n" ) ;
else {
int lca = LCA ( x , y ) ;
printf ( "%d\n" , gmin ( solve( lca , x ) , solve ( lca , y ) ) ) ;
}
}
return ;
}

2016-10-02 19:52:23

()

NOIP 2013 货车运输【Kruskal + 树链剖分 + 线段树 】【倍增】的更多相关文章

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

    题面 一句话题意: 给定一张 N 个点, M 条边的无向连通图, 每条边上有边权 w . 求删去任意一个点后的最小生成树的边权之和. 思路 首先肯定要$kruskal$一下 考虑$MST$里面去掉一个 ...

  2. 【Codeforces827D/CF827D】Best Edge Weight(最小生成树性质+倍增/树链剖分+线段树)

    题目 Codeforces827D 分析 倍增神题--(感谢T*C神犇给我讲qwq) 这道题需要考虑最小生成树的性质.首先随便求出一棵最小生成树,把树边和非树边分开处理. 首先,对于非树边\((u,v ...

  3. 【BZOJ-2325】道馆之战 树链剖分 + 线段树

    2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 1153  Solved: 421[Submit][Statu ...

  4. 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

    [BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...

  5. BZOJ2243 (树链剖分+线段树)

    Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. ...

  6. POJ3237 (树链剖分+线段树)

    Problem Tree (POJ3237) 题目大意 给定一颗树,有边权. 要求支持三种操作: 操作一:更改某条边的权值. 操作二:将某条路径上的边权取反. 操作三:询问某条路径上的最大权值. 解题 ...

  7. bzoj4034 (树链剖分+线段树)

    Problem T2 (bzoj4034 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子 ...

  8. HDU4897 (树链剖分+线段树)

    Problem Little Devil I (HDU4897) 题目大意 给定一棵树,每条边的颜色为黑或白,起始时均为白. 支持3种操作: 操作1:将a->b的路径中的所有边的颜色翻转. 操作 ...

  9. Aizu 2450 Do use segment tree 树链剖分+线段树

    Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show ...

  10. 【POJ3237】Tree(树链剖分+线段树)

    Description You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edg ...

随机推荐

  1. BlueTooth: 蓝牙基础知识进阶——链路控制操作

    转自:http://blog.csdn.net/augusdi/article/details/25887395 七链路控制操作 链路控制操作就是用来描述一个设备是如何加入piconet又是如何从一个 ...

  2. oracle删除用户下所有的表

    需要创建这些删除语句,通过oracle的数据字典找到该用户下的所有表.视图等对象,拼接成语句.如下select 'drop table '||table_name|| ' cascade constr ...

  3. 在ASP.NET 5中使用SignalR

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:SignalR作为ASP.NET中进行Web实时双向通信的组件,在ASP.NET 5中 ...

  4. Codeforces Round #337 (Div. 2) D. Vika and Segments 线段树 矩阵面积并

    D. Vika and Segments     Vika has an infinite sheet of squared paper. Initially all squares are whit ...

  5. 智能车学习(二十二)——浅谈速度控制

    一.经典PID控制       使用遇限反向PID会比较适合有加减速的车子,使用变速积分适合跑匀速的车子.然后这种方法的条件下,一定要尽可能缩短控制周期..   二.PID加棒棒控制       针对 ...

  6. 【Debug 报异常】Debug打断点报错

    用DEBUG启动项目,项目中打断点的,然后会报异常 解决方法: 第一步: 项目-->Java编译器-->Classfile Generation 复选框 全部勾选 第二步: 替换当前文件运 ...

  7. 【MySQL 忘记密码】MySQL忘记密码怎么解决 mysql5.5 windows7

    ---恢复内容开始--- 如果MySQL 长久不使用,忘记密码,怎么解决??? 1.首先,需要在任务管理器关闭mysql相关的服务进程 2.cmd,进入DOS窗口,进入到mysql的安装路径的bin目 ...

  8. EventBus代替Intent将复杂对象传递给下一个即将启动的Activity

    我觉得EventBus确实非常好,把我们从序列化中解脱出来,即使不序列化也能在界面间传递数据,但是有个前提,那是两个界面都已经存在并且注册了EventBus.而即将启动的下一个Activity就非常尴 ...

  9. linux服务器init 5启动图形界面,报错Retrigger failed udev events

    今天因工作需要开启linux系统的桌面环境,使用startx未成功,报如下错误: [root@ /]# startx xauth: creating new authority xinit: No s ...

  10. 分享一个漂亮WPF界面框架创作过程及其源码(转)

    本文会作为一个系列,分为以下部分来介绍: (1)见识一下这个界面框架: (2)界面框架如何进行开发: (3)辅助开发支持:Demo.模板.VsPackage制作. 框架源码如下所示. 本文介绍第(1) ...