Grass Cownoisseur 题解

原题

写一个非常符合直觉的做法,这题肯定要强联通分量缩点,缩完点之后呢?我们发现逆向边只有一条,这启示我们可以通过分层图的方法来解决这个题目。同层连正边,异层连反边即可,边权是每个联通分量的大小。跑一遍 dij 即可。时间复杂度应该是 \(O(n \log m)\) 的,代码实现很简单。

#include<bits/stdc++.h>
#define pii pair<int,int>
using namespace std;
const int N=1e5+5;
int n,vis[N],dfn[N],low[N],st[N],top,sc,id[N],cnt,mark[N],siz[N],f[N],res[N],m;
int dis[N][2],ans;
vector<int>e[N],g[N];
struct node {
	int v,w,fl;
	bool operator <(const node &a)const {
		return w>a.w;
	}
};
vector<node>h[N][2];
pii b[N];
void tarjan(int u) {
	vis[u]=1;
	dfn[u]=low[u]=++cnt;
	st[++top]=u,mark[u]=1;
	for(int p:e[u]) {
		if(!dfn[p])tarjan(p),low[u]=min(low[u],low[p]);
		else if(mark[p])low[u]=min(low[u],dfn[p]);
	}
	if(dfn[u]==low[u]) {
		++sc;
		int y;
		do {
			y=st[top--],siz[sc]++;
			id[y]=sc,mark[y]=0;
			g[sc].push_back(y);
		} while(u!=y);
	}
}
signed main() {
	scanf("%d%d",&n,&m);
	for(int i=1,x,y; i<=m; i++) {
		scanf("%d%d",&x,&y);
		e[x].push_back(y);
		b[i]= {x,y};
	}
	for(int i=1; i<=n; i++)
		if(!vis[i])tarjan(i);
	for(int i=1; i<=m; i++) {
		if(id[b[i].first]==id[b[i].second ])continue;
		h[id[b[i].first]][0].push_back({id[b[i].second],siz[id[b[i].second]],0});
		h[id[b[i].first]][1].push_back({id[b[i].second],siz[id[b[i].second]],1});
		h[id[b[i].second]][0].push_back({id[b[i].first],siz[id[b[i].first]],1});
	}
	memset(dis,-0x3f,sizeof dis);
	dis[id[1]][0]=0;
	priority_queue<node>q;
	q.push({id[1],dis[id[1]][0],0});
	while(!q.empty()) {
		int x=q.top().v,d=q.top().w,fl=q.top().fl;
		q.pop();
		if(d!=dis[x][fl])continue;
		for(auto tmp:h[x][fl]) {
			int v=tmp.v ,w=tmp.w,fl1=tmp.fl;
			if(dis[v][fl1]<dis[x][fl]+w) {
				dis[v][fl1]=dis[x][fl]+w;
				q.push({v,dis[v][fl1],fl1});
				if(v==id[1]) {
					ans=max(ans,dis[v][fl1]);
				}
			}

		}
	}
	cout<<ans<<endl;
	return 0;
}
posted @ 2025-02-21 14:59  hnczy  阅读(23)  评论(0)    收藏  举报