洛谷 P3119 题解

题目传送门


题意简述:

  • \(n\)个点,\(m\)条有向边,求从点\(1\)出发最后回到点\(1\)最多能经过的点数

思路:

  • 首先想到\(tarjan\)缩点,因为可以想到只要走到一个连通块里面,则一定可以走完这个连通块

  • 缩点之后,若是不用走反边,答案即为点\(1\)所在强连通分量点数

  • 若是要走反边:

    • 先从点\(1\)所在连通块出发,求出能到达每个连通块的最长路;再跑一遍反图,求出到达每个连通块的最长路(每条边指向翻转不会影响强连通分量)。

    • (这里考试没细想,直接想用DFS,并且还没更新最长路……寄的很惨……可惜了)

    • (事实上这个最长路拓扑,SPFA等都是可以的)

    • 最后枚举连通块之间的每条边,若是这条边的起点能到达点\(1\)(即刚才跑反图能从点\(1\)到达这里),终点能够从点\(1\)来到,那么把这两个的最长路径之和减去点\(1\)所在连通块点数即可

      • (即\(dis2[now]+dis1[to]-num[belong[1]]\)

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<stack>
#include<vector>
#include<bitset>
#include<queue>
#define int long long
#define maxn 100005
using namespace std;
void read(int &n){
	n=0;int f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		n=(n<<3)+(n<<1)+ch-'0';
		ch=getchar();
	}
	n*=f;
}

struct EDGE{
	int to,next;
	bool opt;
}edge[maxn<<2];
int head[maxn<<2];
int cnt_edge=0;
void init(int n){
	for(int i=1;i<=n;i++) head[i]=-1;
}

void add(int u,int v,bool pd){
	if(pd==true) edge[cnt_edge].opt=true;
	else edge[cnt_edge].opt=false;
	edge[cnt_edge].to=v;
	edge[cnt_edge].next=head[u];
	head[u]=cnt_edge++;
}

int n,m;
int dfn[maxn],low[maxn],cnt_tar=0;
stack <int> st;bool inz[maxn];
void init2(){for(int i=0;i<maxn;i++) inz[i]=false;}
int belong[maxn],num[maxn],cnt_be=0;

void tarjan(int rt){
	dfn[rt]=++cnt_tar;low[rt]=dfn[rt];
	st.push(rt);inz[rt]=true;
	int to;
	for(int i=head[rt];i>=0;i=edge[i].next){
		if(edge[i].opt==false) continue;
		to=edge[i].to;
		if(!dfn[to]){
			tarjan(to);
			low[rt]=min(low[rt],low[to]);
		}else if(inz[to]==true){
			low[rt]=min(low[rt],dfn[to]);
		}
	}
	int now=-1;
	if(dfn[rt]==low[rt]){
		cnt_be++;
		while(now!=rt){
			now=st.top(),st.pop();
			inz[now]=false;
			belong[now]=cnt_be;
			num[cnt_be]++;
		}
	}
	return ;
}

struct EDGE_VE{
	int to;
	bool opt;
};
vector <EDGE_VE> g[maxn];
int fro[maxn],too[maxn];
int in[maxn],ou[maxn];

bool vis[maxn];

void dfs1(int rt,bool pd){
	vis[rt]=true;
	int to;
	for(int j=0;j<g[rt].size();j++){
		if(g[rt][j].opt!=pd) continue;
		to=g[rt][j].to;
		if(!vis[to]){
			dfs1(to,pd);
		}
	}
}

void topo1(int rt,int pd){
	memset(in,0,sizeof(in));
	memset(ou,0,sizeof(ou));
	int to;
	for(int i=1;i<=cnt_be;i++){
		if(!vis[i]) continue;
		for(int j=0;j<g[i].size();j++){
			to=g[i][j].to;
			if(g[i][j].opt==pd&&vis[to]==true){
				ou[i]++,in[to]++;
			}
		}
	} 
	queue <int> q;
	q.push(belong[1]);
	fro[belong[1]]=num[belong[1]];
	int now;
	while(!q.empty()){
		now=q.front(),q.pop();
		for(int j=0;j<g[now].size();j++){
			if(g[now][j].opt==pd){
				to=g[now][j].to;
				fro[to]=max(fro[to],fro[now]+num[to]);
				in[to]--;
				if(!in[to]){
					q.push(to);
				}
			}
		}
	}
}

void dfs2(int rt,bool pd){
	vis[rt]=true;
	int to;
	for(int j=0;j<g[rt].size();j++){
		if(g[rt][j].opt!=pd) continue;
		to=g[rt][j].to;
		if(!vis[to]){
			dfs2(to,pd);
		}
	}
}

void topo2(int rt,int pd){
	memset(in,0,sizeof(in));
	memset(ou,0,sizeof(ou));
	int to;
	for(int i=1;i<=cnt_be;i++){
		if(!vis[i]) continue;
		for(int j=0;j<g[i].size();j++){
			to=g[i][j].to;
			if(g[i][j].opt==pd&&vis[to]){
				ou[i]++,in[to]++;
			}
		}
	} 
	queue <int> q;
	q.push(belong[1]);
	too[belong[1]]=num[belong[1]];
	int now;
	while(!q.empty()){
		now=q.front(),q.pop();
		for(int j=0;j<g[now].size();j++){
			if(g[now][j].opt==pd){
				to=g[now][j].to;
				too[to]=max(too[to],too[now]+num[to]);
				in[to]--;
				if(!in[to]){
					q.push(to);
				}
			}
		}
	}
}

signed main()
{
//	freopen("wander.in","r",stdin);
//	freopen("wander.out","w",stdout);
	read(n),read(m);
	init(n);
	init2();
	int a,b;
	for(int i=1;i<=m;i++){
		read(a),read(b);
		add(a,b,true);
		add(b,a,false);
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i]) tarjan(i);
	}
	int to;
	for(int i=1;i<=n;i++){
		for(int j=head[i];j>=0;j=edge[j].next){
			if(belong[i]==belong[edge[j].to]) continue;
			g[belong[i]].push_back((EDGE_VE){belong[edge[j].to],edge[j].opt});
		}
	}
	dfs1(belong[1],true);
	topo1(belong[1],true);
	memset(vis,0,sizeof(vis));
	dfs2(belong[1],false);
	topo2(belong[1],false);
	int ans=num[belong[1]];
	for(int i=1;i<=cnt_be;i++){
		for(int j=0;j<g[i].size();j++){
			if(g[i][j].opt==true) continue;
			to=g[i][j].to;
			if(!too[to]||!fro[i]) continue;
			ans=max(ans,too[to]+fro[i]-num[belong[1]]);
		}
	}
	printf("%lld",ans);
	return 0;
}

原发布于 \(2023.2.5\)

posted @ 2023-08-09 20:28  lnbyn  阅读(8)  评论(0编辑  收藏  举报