NOIP 2013 货车运输【Kruskal + 树链剖分 + 线段树 】【倍增】

时间:2023-01-23 13:16:50

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,可以视为加入一个虚拟节点(如图) ,再用线段树维护即可。

NOIP 2013 货车运输【Kruskal + 树链剖分 + 线段树 】【倍增】

注释&代码:

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. &lbrack;HDU3710&rsqb; Battle Over Cities &lbrack;树链剖分&plus;线段树&plus;并查集&plus;kruskal&plus;思维&rsqb;

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

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

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

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

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

  4. 【BZOJ2243】&lbrack;SDOI2011&rsqb;染色 树链剖分&plus;线段树

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

  5. BZOJ2243 &lpar;树链剖分&plus;线段树)

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

  6. POJ3237 &lpar;树链剖分&plus;线段树)

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

  7. bzoj4034 (树链剖分&plus;线段树)

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

  8. HDU4897 (树链剖分&plus;线段树)

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

  9. Aizu&Tab;2450 Do use segment tree 树链剖分&plus;线段树

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

  10. 【POJ3237】Tree(树链剖分&plus;线段树)

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

随机推荐

  1. &lbrack;No000031&rsqb;操作系统 Operating Systems 之Open the OS&excl;

    从打开电源开始… 这神秘的黑色背后发生着什么?… 打开电源,计算机执行的第一句指令什么? 计算模型(图灵机) ⇒ 我们要 关注 指针IP 及其 指向的内容 看看x86 PC (1) 刚开机时CPU 处 ...

  2. 4817 *的dp题d

    4817 *的dp题d  时间限制: 1 s  空间限制: 256000 KB  题目等级 : 黄金 Gold 题解       题目描述 Description 已知1-N的排列P的LIS(最长上 ...

  3. &lpar;leetcode&rpar;Summary Ranges

    Given a sorted integer array without duplicates, return the summary of its ranges. For example, give ...

  4. 手机web开发Repeater四层嵌套

    最近有朋友想让我给他做个手机上页面,页面功能是显示省--市--区--门店信息,这种层级关系的数据,首先来看看效果: 我想现在的手机都是智能机了对于普通的asp.net页面开发应该没什么两样,不过最终开 ...

  5. jsp请求由servlet响应的方式

    一.登录页面主要代码:login.jsp<%@ page language="java" import="java.util.*" pageEncodin ...

  6. github 分支 合并

    Git如何进行分支管理?      1.创建分支      创建分支很简单:git branch <分支名>      2.切换分支      git checkout <分支名&g ...

  7. 【Android Developers Training】 25&period; 保存文件

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  8. 软件工程个人第二小项目——wc

    github源码和工程文件地址:https://github.com/HuChengLing/wc 基本要求:要实现wc的基本功能即文件中字符数.单词数.行数的统计. 主要功能:文件中字符数.单词数. ...

  9. javascript三角函数的使用

    其实很多编程语言里面都有数学函数,而且很多数学函数包括三角函数,只不过有些时候可能我们用的并不多,我最近在做一个h5的游戏,其中有一个需求就是射击的枪支需要更随鼠标变换位置,鼠标移动到什么地方,炮口就 ...

  10. SQL注入渗透实战

    概述: 判断注入点: http://www.xxxxx.com/page.php?pid=42 and 1=1 #true http://www.xxxxx.com/page.php?pid=42 a ...