1. 程式人生 > 實用技巧 >Solution -「CF 1023F」Mobile Phone Network

Solution -「CF 1023F」Mobile Phone Network

\(\mathcal{Description}\)

  Link.

  有一個 \(n\) 個結點的圖,並給定 \(m_1\) 條無向帶權黑邊,\(m_2\) 條無向無權白邊。你需要為每條白邊指定邊權,最大化其邊權和,並保證 \(m_2\) 條邊都在最小生成樹中。

  \(n,m_1,m_2\le5\times10^5\)

\(\mathcal{Solution}\)

  先保證在 \(\text{MST}\) 中的限制——指定所有邊權為 \(0\)。並求出此時的 \(\text{MST}\)。顯然最優情況下,\(\text{MST}\) 的形態和現在一樣。

  那麼對於每一條不在 \(\text{MST}\)

上的黑邊,相當於限制了一條樹上路徑的最大值。從小到大列舉這樣的邊,每個點維護一個指標(整體上就是一個並查集)指向第一個未被限制到父親邊權的祖先,暴力跳指標統計答案即可。

  複雜度 \(\mathcal O((m_1+m_2)\log(m_1+m_2))\)

\(\mathcal{Code}\)

#include <cstdio>
#include <algorithm>

#define ww first
#define uu second.first
#define vv second.second

const int MAXN = 5e5, MAXM = 1e6;
int n, m1, m2, m, fa[MAXN + 5];
int ecnt, head[MAXN + 5], trf[MAXN + 5], trc[MAXN + 5], dep[MAXN + 5];
std::pair<int, std::pair<int, int> > eset[MAXM + 5];
bool inmst[MAXM + 5];

struct Edge { int to, cst, nxt; } graph[MAXN * 2 + 5];

inline int rint () {
	int x = 0; char s = getchar ();
	for ( ; s < '0' || '9' < s; s = getchar () );
	for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' );
	return x;
}

inline void link ( const int s, const int t, const int c ) {
	graph[++ ecnt] = { t, c, head[s] };
	head[s] = ecnt;
}

inline void init () { for ( int i = 1; i <= n; ++ i ) fa[i] = i; }

inline int find ( const int x ) { return x ^ fa[x] ? fa[x] = find ( fa[x] ) : x; }

inline bool unite ( int x, int y ) {
	return ( x = find ( x ) ) ^ ( y = find ( y ) ) ? fa[x] = y, true : false;
}

inline void DFS ( const int u ) {
	for ( int i = head[u], v; i; i = graph[i].nxt ) {
		if ( ( v = graph[i].to ) ^ trf[u] ) {
			trf[v] = u, trc[v] = graph[i].cst, dep[v] = dep[u] + 1;
			DFS ( v );
		}
	}
}

int main () {
	n = rint (), m = ( m1 = rint () ) + ( m2 = rint () );
	init ();
	for ( int i = 1; i <= m1; ++ i ) {
		eset[i].uu = rint (), eset[i].vv = rint ();
		eset[i].ww = 0;
	}
	for ( int i = m1 + 1; i <= m; ++ i ) {
		eset[i].uu = rint (), eset[i].vv = rint ();
		eset[i].ww = rint ();
	}
	std::sort ( eset + 1, eset + m + 1 );
	for ( int i = 1, cnt = 0; i <= m; ++ i ) {
		if ( unite ( eset[i].uu, eset[i].vv ) ) {
			inmst[i] = true;
			link ( eset[i].uu, eset[i].vv, eset[i].ww );
			link ( eset[i].vv, eset[i].uu, eset[i].ww );
			if ( ++ cnt == n - 1 ) break;
		}
	}
	DFS ( 1 ), init ();
	long long ans = 0; int limited = 0;
	for ( int i = m1 + 1; i <= m; ++ i ) {
		if ( inmst[i] ) continue;
		int u = find ( eset[i].uu ), v = find ( eset[i].vv ), w = eset[i].ww;
		while ( u ^ v ) {
			if ( dep[u] < dep[v] ) u ^= v ^= u ^= v;
			if ( ! trc[u] ) ans += w, ++ limited;
			int t = find ( trf[u] );
			unite ( u, t ), u = t;
		}
	}
	printf ( "%lld\n", limited == m1 ? ans : -1 );
	return 0;
}