1. 程式人生 > 其它 >「CEOI2012」 Network 題解

「CEOI2012」 Network 題解

「CEOI2012」 Network 題解

題意

\(~~~~\) 給出一幅 \(n\) 個點,\(m\) 條邊的有向圖。定義從 \(p\) 可以到達 \(q\),當且僅當不存在兩條從 \(p\)\(q\) 的路徑其交集為空。同時保證圖中有一個節點 \(r\) ,它可以到達所有點。求出每個頂點可以到達多少個頂點(A)以及最少的加邊方案使得任意兩個頂點互相可達(B)。同時保證B問題有解。

\(~~~~\) \(1\leq n\leq 10^5,1\leq m\leq 5\times 10^5\)

本文版權歸 Azazel 與部落格園共有,歡迎轉載,但需保留此宣告,並給出原文地址,謝謝合作。

原文地址:https://www.cnblogs.com/Azazel/p/15240429.html

題解

\(~~~~\) 根據題解反推題意太草了。

\(~~~~\) 本題重點在於找出原始圖的形態特點並考慮縮點後及求解過程中將會保持的形態特點,之後的問題將迎刃而解。

\(~~~~\) 由於保證B問題有解,即初始給出的圖不會使得某個兩個點之間有兩條不相交的路徑,因此我們可以推出原圖的形態為一棵樹加上若干條返祖邊。考慮若某條邊 \(u\rightarrow v\) 不是返祖邊,則這兩個點的 LCA 到 \(v\) 的路徑會不唯一,故證得所有非樹邊必定為返祖邊。

\(~~~~\) 先來思考怎麼做問題A,由於一個環內的點的答案都是一樣的,所以我們可以縮點來統計答案。由於有上面關於圖的形態的保證,縮點後得到的圖一定是一棵以 \(r\)

為根的有根外向樹。因此每一個縮點後的強連通分量的答案即為其子樹內原節點的個數和。

\(~~~~\) 再來考慮問題B,問題B即讓我們把所有點串成一個強連通分量。由於有上面的分析,我們加入的邊也一定都是返祖邊。因此我們需要做的是加入最少的返祖邊使得每條邊僅剛好屬於一個簡單環。(若屬於多個環則必定會出現一對點之間的路徑不唯一)考慮貪心覆蓋,則每個點只需要暴力往上跳所有未被簡單環包含的邊並向最後跳到的點連邊即可。為了同時保證選擇的初始點最優,我們每次選擇非環邊出度最小的點,進行類似拓撲排序的連邊即可。

\(~~~~\) 時間複雜度:\(\mathcal{O(n+m)}\)

程式碼

\(~~~~\)

本題應該也不會有人拿別人程式碼去對拍,所以這裡放出的程式碼略去了部分無關緊要的地方。

檢視(屎山)程式碼
#define PII pair<int,int>
#define mp(a,b) make_pair(a,b)
using namespace std;
int n,m,r;
stack<int>S;
bool InStack[100005];
int dfn[100005],low[100005],Times,tot,Out[100005];
int dep[100005],Ans1[100005],belong[100005],siz[100005],Fa[100005];
vector < PII > G[100005],G_[100005];
void Tarjan(int u){··· ···}
void dfs(int u,int fa){··· ···}//求解問題1的dfs
struct Edge{
	int u,v;
	Edge(){}
	Edge(int U,int V){u=U,v=V;}
}E[500005];
queue<int>q;
map<int,bool>vis[100005];
void Tag(int u,int fa)
{
	Fa[u]=fa;
	dep[u]=dep[fa]+1;
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i].first;
		if(dep[v]<dep[u]&&dep[v]) continue;//注意不能走返祖邊,因為此處為找父節點,且返祖邊必定包含在環內,不需考慮
		Tag(v,u);
	}
}
vector < PII > Ans2;
void TopSort()
{
	for(int i=1;i<=n;i++) if(!Out[i]) q.push(i);
	while(!q.empty())
	{
		int u=q.front();q.pop();
		int x=u;
		while(x!=r)
		{
			if(vis[x][Fa[x]]) break;
			vis[x][Fa[x]]=true;x=Fa[x];
		}
		if(x&&x!=u) Ans2.push_back(mp(u,x));//注意沒有跳時不需要連邊
		if((--Out[x])==0) q.push(x);
	}
}
template<typename T>void read(T &x){··· ···}//快讀
template<typename T>void print(T x){··· ···}//快輸
int main() {
	read(n);read(m);read(r);
	for(int i=1,u,v;i<=m;i++)
	{
		read(u);read(v);
		E[i]=Edge(u,v);G[u].push_back(mp(v,i));
	}
	for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i);
	for(int i=1;i<=n;i++) G[i].clear();
	for(int i=1;i<=m;i++)
	{
		int x=belong[E[i].u],y=belong[E[i].v];
		if(x!=y) G[x].push_back(mp(y,i)); 
	}
	dfs(belong[r],0);
	for(int i=1;i<=n;i++) print(Ans1[belong[i]]),putchar(' ');
	puts("");
	for(int i=1;i<=tot;i++) G[i].clear();
	for(int i=1;i<=m;i++)
	{
		G[E[i].u].push_back(mp(E[i].v,i));
		G_[E[i].v].push_back(mp(E[i].u,i));
		if(belong[E[i].u]==belong[E[i].v]) {vis[E[i].u][E[i].v]=vis[E[i].v][E[i].u]=true;continue;}
		Out[E[i].u]++;
	}
	Tag(r,0);TopSort();
	print(Ans2.size());puts("");
	for(int i=0;i<Ans2.size();i++) print(Ans2[i].first),putchar(' '),print(Ans2[i].second),puts("");
	return 0;
}