[CF732F]Tourist Reform

题目

传送门 to luogu

思路

对于一个边双连通分量,显然应该将其构成一个大环,因为这样一来每个点能到达的地方最多。

缩点结束,剩下一颗树。难道是树形 d p \tt dp 吗?或者二分答案?都很难做。

没想到是第二次贪心,对于树,总出度只有 n 1 n-1 ,所以 一定有一个点无出度。那么答案一定不会超过这个点的权值(也就是这个边双的大小)。能不能取等?可以。所有点都走向它即可。

所以我们可以直接构造出最终答案,那就是最大的一个边双。桥边很好确定方向。其余的呢?

直接按照 d f s \tt dfs 顺序走即可,因为 d f s \tt dfs 的过程本身就会走回祖先。

代码

#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef long long int_;
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
template < typename T >
void getMin(T&a,const T&b){ if(b<a)a=b; }

const int MaxN = 400005;
int n, m;

struct Edge{
	int to, nxt;
	Edge(){ }
	Edge(int T,int N){
		to = T, nxt = N;
	}
};
Edge e[MaxN<<2];
int head[MaxN], cntEdge;
void clear(){
	for(int i=1; i<=n; ++i)
		head[i] = -1;
	cntEdge = 0;
}
/** @brief add undirected edge */
void addEdge(int a,int b){
	e[cntEdge] = Edge(b,head[a]);
	head[a] = cntEdge ++;
	e[cntEdge] = Edge(a,head[b]);
	head[b] = cntEdge ++;
}

bool inSta[MaxN], used[MaxN<<1];
vector< int > sta;
int bel[MaxN], val[MaxN];
int dfn[MaxN], low[MaxN], dfsClock;
void tarjan(int x,int pre){
	dfn[x] = low[x] = ++ dfsClock;
	inSta[x] = 1, sta.push_back(x);
	for(int i=head[x]; ~i; i=e[i].nxt){
		if(e[i].to == pre) continue;
		used[i] = true;
		if(!dfn[e[i].to]){
			tarjan(e[i].to,x);
			getMin(low[x],low[e[i].to]);
		}
		else if(dfn[e[i].to] < dfn[x])
			getMin(low[x],dfn[e[i].to]);
		else used[i] = false;
	}
	if(low[x] == dfn[x]){
		int y; do{
			y = sta.back(), sta.pop_back();
			bel[y] = x, inSta[y] = 0;
			++ val[x]; // 每个点权值为 1
		}while(y != x);
	}
}

int dep[MaxN]; // 便于确定方向
void dfs(int x,int pre){
	for(int i=head[x]; ~i; i=e[i].nxt)
		if(e[i].to != pre){
			dep[e[i].to] = dep[x]+1;
			dfs(e[i].to,x);
		}
}

int main(){
	n = readint(), m = readint();
	clear(); // just do it, man.
	for(int i=1; i<=m; ++i)
		addEdge(readint(),readint());
	tarjan(1,-1);
	for(int i=1; i<=n; ++i)
		head[i] = -1; // 原边保留
	for(int i=0; i<(m<<1); ++i)
		if(used[i] && bel[e[i].to] != bel[e[i^1].to])
			addEdge(bel[e[i].to],bel[e[i^1].to]);
	int rt = 0; // 根节点
	for(int i=1; i<=n; ++i)
		if(val[i] > val[rt]) rt = i;
	dfs(rt,-1);
// puts("ANS");
	printf("%d\n",val[rt]);
	for(int i=0; i<(m<<1); i+=2)
		if(used[i] || used[i^1]){
			if(bel[e[i].to] != bel[e[i^1].to])
				if(dep[bel[e[i].to]] > dep[bel[e[i^1].to]])
					printf("%d %d\n",e[i].to,e[i^1].to);
				else
					printf("%d %d\n",e[i^1].to,e[i].to);
			else if(used[i])
				printf("%d %d\n",e[i^1].to,e[i].to);
			else printf("%d %d\n",e[i].to,e[i^1].to);
		}
		else printf("%d %d\n",e[i^1].to,e[i].to);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42101694/article/details/108132849