1. 程式人生 > 實用技巧 >[SCOI2012]滑雪

[SCOI2012]滑雪

題目

點這裡看題目。

分析

首先第一問非常簡單,可以直接 BFS 解決。

考慮第二問,類似於生成樹,可以暴力朱劉演算法解決顯然我們只需要對 BFS 中遇到的點和邊進行生成樹。這裡的邊需要保證有 \(h_u\ge h_v\)

注意到這些點是必選的,因此我們只需要保證在生成樹構建的過程中總有加入的邊是合法的,就可以把邊近似地看作無向邊。

因此可以直接對於每條邊,按照終點高度優先從高到低、邊權次之由低到高排序並進行 Kruskal 。

由於可到的點必然可以按照高度分層,因此我們實際上是在分層進行 Kruskal ,所以每次每條邊的起點必然已經被加入。

小結:

利用高度帶來的層的性質,將有向邊轉化為無向邊

,從而可以進行 Kruskal 。

程式碼

#include <cstdio>
#include <algorithm>
using namespace std;

#define rep( i, a, b ) for( int (i) = (a) ; (i) <= (b) ; ++ (i) )
#define per( i, a, b ) for( int (i) = (a) ; (i) >= (b) ; -- (i) )

typedef long long LL;

const int MAXN = 2e5 + 5, MAXM = 1e6 + 5;

template<typename _T>
void read( _T &x )
{
	x = 0; char s = getchar(); int f = 1;
	while( s < '0' || '9' < s ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ); s = getchar(); }
	x *= f;
}

template<typename _T>
void write( _T x )
{
	if( x < 0 ) putchar( '-' ), x = -x;
	if( 9 < x ) write( x / 10 );
	putchar( x % 10 + '0' );
}

template<typename _T>
void swapp( _T &x, _T &y )
{
	_T t = x; x = y, y = t;
}

struct Edge
{
	int to, nxt;
}Graph[MAXM];

struct KruEdge
{
	int u, v, w, ed;
	KruEdge() { u = v = w = 0; }
	KruEdge( int U, int V, int W ) { u = U, v = V, w = W; }
	bool operator < ( const KruEdge &b ) const { return ed == b.ed ? w < b.w : ed > b.ed; }
}E[MAXM];

int q[MAXN];

int head[MAXN], fa[MAXN], H[MAXN];
int N, M, K, cnt = 0;
bool vis[MAXN];

void MakeSet( const int siz ) { rep( i, 1, N ) fa[i] = i; }
int FindSet( const int u ) { return fa[u] = ( fa[u] == u ? u : FindSet( fa[u] ) ); }

bool UnionSet( int u, int v )
{
	u = FindSet( u ), v = FindSet( v );
	if( u == v ) return false; fa[u] = v; return true;
}

void AddEdge( const int from, const int to )
{
	Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
	head[from] = cnt;
}

int main()
{
	read( N ), read( M );
	rep( i, 1, N ) read( H[i] );
	rep( i, 1, M ) 
	{
		read( E[i].u ), read( E[i].v ), read( E[i].w );
		if( H[E[i].u] < H[E[i].v] ) continue;
		AddEdge( E[i].u, E[i].v ), E[i].ed = H[E[i].v];
	}
	int h = 1, t = 0; vis[q[++ t] = 1] = true;
	while( h <= t )
	{
		int u = q[h ++], v;
		for( int i = head[u] ; i ; i = Graph[i].nxt )
			if( ! vis[v = Graph[i].to] ) vis[q[++ t] = v] = true;
	}
	write( t ), putchar( ' ' ); LL ans = 0;
	sort( E + 1, E + 1 + M ); MakeSet( N );
	rep( i, 1, M ) 
		if( H[E[i].u] >= H[E[i].v] )
			if( vis[E[i].u] && vis[E[i].v] && UnionSet( E[i].u, E[i].v ) )
				ans += E[i].w;
	write( ans ), putchar( '\n' );
	return 0;
}