luogu P3119 [USACO15JAN]草鉴定Grass Cownoisseur

题目传送门:https://www.luogu.org/problemnew/show/P3119



题意:

在一个有向图中,有n个点,m条边,可以逆行一条边,求从1走到任意点,再返回所经过的最多点的个数。



思路:

先缩点,再跑两边spfa。

我们可以知道,缩完点后剩下的图中有三种点

1.与1点直接相连;

2.与1点通过反向边相连;

3.与1点不相连。

而因为题目说可以逆行一条边,所以3这种点是完全没有用的。而我们可以对1,2这种点进行操作,统计当前这个点跑正向spfa与反向spfa的和的最大值(注意用size来更新dis,其中size[i]表示i这个强联通分量的大小,dis[i]表示到这个点的最短路的权值),因为1点重复计算,所以最后再减去size[1]即可。



代码:

#include
#include
#include
#define _ 100010
using namespace std;
queue f;
	struct node1{int x,y,next;} a[_],b[_];
	int n,m,lena=0,lenb=0,id=0,tp=0,cnt=0,ans=0;
	int lasta[_],lastb[_],belong[_],low[_],dfn[_],sta[_],size[_],dis[2][_];
	bool insta[_];
void ins(int x,int y)
{
	a[++lena].x=x;a[lena].y=y;a[lena].next=lasta[x];lasta[x]=lena;
}
void dfs(int x)
{
	low[x]=dfn[x]=++id;
	sta[++tp]=x;
	insta[x]=true;
	for(int k=lasta[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if(dfn[y]==-1)
		{
			dfs(y);
			if(low[x]>low[y]) low[x]=low[y];
		}
		else if(insta[y]&&low[x]>dfn[y]) low[x]=dfn[y];
	}
	if(low[x]==dfn[x])
	{
		int i;
		cnt++;
		do{
			i=sta[tp--];
			insta[i]=false;
			belong[i]=cnt;
			size[cnt]++;
		}while(i!=x);
	}
}
void spfa(int id)
{
	memset(dis[id],-1,sizeof(dis[id]));
	dis[id][belong[1]]=size[belong[1]];
	f.push(belong[1]);
	while(!f.empty())
	{
		int x=f.front();
		for(int i=lastb[x];i;i=b[i].next)
		{
			int y=b[i].y;
			if(dis[id][y]

你可能感兴趣的:(#,spfa,#,强联通)