1. 程式人生 > >【BZOJ - 3832】[Poi2014] Rally

【BZOJ - 3832】[Poi2014] Rally

[Poi2014] Rally


@[email protected]

給定一個N個點M條邊的有向無環圖,每條邊長度都是1。
請找到一個點,使得刪掉這個點後剩餘的圖中的最長路徑最短。

Input
第一行包含兩個正整數 N , M ( 2 < = N <

= 500000 , 1 < = M < = 1000000
) N,M(2<=N<=500 000,1<=M<=1 000 000) ,表示點數、邊數。
接下來M行每行包含兩個正整數 A [ i ] , B [ i ] ( 1 < = A [ i ] , B [ i ] < = N , A [ i ] ̸ = B [ i ] ) A[i],B[i](1<=A[i],B[i]<=N,A[i]\not =B[i]) ,表示A[i]到B[i]有一條邊。

Output
包含一行兩個整數x,y,用一個空格隔開,x為要刪去的點,y為刪除x後圖中的最長路徑的長度,如果有多組解請輸出任意一組。

Sample Input
6 5
1 3
1 4
3 6
3 4
4 5
Sample Output
1 2

@Solution - Part [email protected]

【先膜拜想出來這道題的大佬們 orz】
【太神了,要我一輩子都想不出來這種題QAQ】
首先處理圖不連通的情況:建立超級源點 s 向所有入度為 0 的點連邊,建立超級匯點 t 使所有出度為 0 的點向 t 連邊。

然後看一張圖:
題解圖示

紅線是該圖的一個“割”:將紅線上的邊刪除過後,原圖形成兩個連通塊且兩個連通塊分別包含源點 s 與匯點 t 。
我們令包含源點 s 的連通塊為集合 S,包含匯點 t 的為集合 T。

為什麼要畫這樣一條“割”呢?可以發現有這樣一個性質:原圖中 s 到 t 的每一條路徑恰好包含“割”上的一條邊。
反過來,我們可以令一條邊的 tag = “經過這條邊的路徑長度的最大值”,再用一個數據結構儲存“割”上的所有邊的 tag。
然後——假如我們要求解刪去某一條“割”邊的最長路,等價於在資料結構中刪除這條邊的 tag 後,求資料結構中的最大 tag

刪去某一個點,等價於刪除它所有的入邊。

這個資料結構應該支援(1)加入與刪除(2)求最大值。
可以用堆,也可以像我一樣直接用 multiset【常數大但好寫www】。

也就是說:我們只需要使得“割”經過一個點的所有入邊,就可以在 O(log n) 時間內求解刪去某一個點的答案。

@Solution - Part [email protected]

具體來講,我們怎麼使得“割”經過一個點的入邊呢?
可以用拓撲序來進行這個過程。

我們按照拓撲序,依此遍歷每一個結點。
對於一個結點 i:先將它所有的入邊從“割”中刪除,求“割”中的最大值,再將它所有的出邊加入“割”。

為什麼這樣是對的?
在這裡插入圖片描述
首先:可以發現這樣操作以後,“割”仍然合法。
然後:因為是按照拓撲序進行的,所以 i 的入邊連線的點 j 在之前一定已經處理過了。i 的入邊作為 j 的出邊,在處理 j 的時候就會加入“割”,所以 i 的所有入邊此時肯定是在“割”內的。

所以這樣是正確的。

@Some [email protected]

怎麼求解一條邊(u, v)的 tag?
可以用 DAG 上 dp 處理出 s 到 u 的最長路與 v 到 t 的最長路。

記住 s, t 是虛點,所以要消除它們對答案的影響。

可能出現這種情況:刪除一個點所有的入邊後,s 和 t 不連通了!
這時候的最長路 = max(s到集合S中的點的最長路, 集合T中的點到t的最長路)
【還記得集合S和T嗎?不記得往上翻一翻】
再開兩個堆來維護即可。

@[email protected]

在 bzoj 上能過,但是在本地 T 得很慘。
建議大家還是寫一寫帶刪除的堆吧。

#include<set>
#include<queue>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN = 500000;
const int MAXM = 1000000;
const int INF = (1<<30);
int read() {
	int x = 0, f = 1; char ch = getchar();
	while( (ch > '9' || ch < '0') && ch != '-' ) ch = getchar();
	if( ch == '-' ) {f = -1; ch = getchar();}
	while( '0' <= ch && ch <= '9' ) {x = x*10 + ch-'0'; ch = getchar();}
	return x * f;
}
struct edge{
	int to;
	edge *nxt;
}edges[2*MAXM + 5], *adj[MAXN + 5], *ecnt;
int n, m, idg[MAXN + 5], odg[MAXN + 5];
int tp[MAXN + 5], tcnt = 0;
vector<int>G[MAXN + 5];
multiset<int, greater<int> >S, T, E;
void init() {
	ecnt = &edges[0];
	for(int i=0;i<=n+1;i++) {
		adj[i] = NULL;
		G[i].clear();
		idg[i] = odg[i] = 0;
	}
	S.clear(); T.clear(); E.clear();
}
void addedge(int u, int v) {
	edge *p = (++ecnt);
	p->to = v, p->nxt = adj[u], adj[u] = p;
	G[v].push_back(u);
}
void tpsort() {
	tcnt = 0;
	for(int i=0;i<=n+1;i++)
		idg[i] = 0;
	for(int i=0;i<=n+1;i++)
		for(edge *p=adj[i];p!=NULL;p=p->nxt)
			idg[p->to]++;
	queue<int>que; que.push(0);
	while( !que.empty() ) {
		int f = que.front(); que.pop(); tp[tcnt++] = f;
		for(edge *p=adj[f];p!=NULL;p=p->nxt)
			if( !(--idg[p->to]) ) que.push(p->to);
	}
}
int f[MAXN + 5], g[MAXN + 5];
int GetAnswer() {
	int ret = 0;
	if( !S.empty() )
		ret = max(ret, *S.begin());
	if( !T.empty() )
		ret = max(ret, *T.begin());
	if( !E.empty() )
		ret = max(ret, *E.begin());
	return ret;
}
void solve() {
	n = read(), m = read(); init();
	for(int i=1;i<=m;i++) {
		int u, v;
		u = read(), v = read();
		addedge(u, v);
		idg[v]++; odg[u]++;
	}
	for(int i=1;i<=n;i++) {
		if( !idg[i] ) addedge(0, i);
		if( !odg[i] ) addedge(i, n+1);
	}
	tpsort(); f[0] = g[n+1] = 0;
	for(int i=1;i<=n+1;i++) {
		f[tp[i]] = 0;
		for(int j=0;j<G[tp[i]].size();j++)
			f[tp[i]] = max(f[tp[i]], f[G[tp[i]][j]] + 1);
	}
	for(int i=n;i>=0;i--) {
		g[tp[i]] = 0;
		for(edge *p=adj[tp[i]];p!=NULL;p=p->nxt)
			g[tp[i]] = max(g[tp[i]], g[p->to] + 1);
	}
	for(int i=0;i<=n+1;i++)
		T.insert(g[i]-1);
	int ans = INF, num;
	for(int i=0;i<=n+1;i++) {
		for(int j=0;j<G[tp[i]].size();j++)
			E.erase(E.find(f[G[tp[i]][j]]+g[tp[i]]-1));
		T.erase(T.find(g[tp[i]]-1));
		if( tp[i] != 0 && tp[i] != n+1 ) {
			int x = GetAnswer();
			if( x < ans ) ans = x, num = tp[i];
			else if( x == ans && num > tp[i] ) num = tp[i];
		}
		for(edge *p=adj[tp[i]];p!=NULL;p=p->nxt)
			E.insert(f[tp[i]]+g[p->to]-1);
		S.insert(f[tp[i]]-1);
	}
	printf("%d %d\n", num, ans);
}
int main() {
	solve();
}

@[email protected]

就是這樣,新的一天裡,也請多多關照哦(ノω<。)ノ))☆.。