P3119 [USACO15JAN]草鉴定Grass Cownoisseur

P3119 [USACO15JAN]草鉴定Grass Cownoisseur


如果不可以反向走的话。这个题的难度就如此的变态。

先不考虑逆向走。

然后我们先分析一波这个图的。可以讲图中的点分为三类。

~标号为1的点

~能到达1的点

~能从1点到达的点

很显然,若只有能到达1的点,或者只有从1可以到达的点。这个图就无法从1出发,然后再回到1。

然后上面两部分点肯定不能所载一起。但是如果我们逆向走的话。就相当从1可以到达的点中,引出了一条连向可以到达1的点的一条边。就构成了一个环。

所以我们就可以枚举一下满足连接上这两部分点的边,然后取一个最优值

具体代码实现的话就是tarjan+spfa(两次,求出两部分的点和)

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int maxn=101000;
struct node
{
	int from;
	int point;
	int nxt;
};
node L1[maxn<<1],L2[maxn<<1],L3[maxn<<1];
int H1[maxn],H2[maxn],H3[maxn],T1,T2,T3;
int belong[maxn],num[maxn],cnt;
int Map[maxn];
int dfn[maxn],low[maxn],t;
int stack[maxn],top;
int insta[maxn];
vector<int>Contain[maxn];
bool inque[maxn];
int Dis1[maxn],Dis2[maxn],ans;
void add1(int a,int b)
{
	L1[++T1].point=b;
	L1[T1].nxt=H1[a];
	H1[a]=T1;
}
void add2(int a,int b)
{
	L2[++T2].point=b;
	L2[T2].nxt=H2[a];
	H2[a]=T2;
}
void add3(int a,int b)
{
	L3[++T3].from=a;
	L3[T3].point=b;
	L3[T3].nxt=H3[a];
	H3[a]=T3;
}
void tarjan(int now)
{
	dfn[now]=low[now]=++t;
	stack[++top]=now;
	insta[now]=true;
	for(int i=H1[now];i;i=L1[i].nxt)
	{
		int nxt=L1[i].point;
		if(!dfn[nxt])
		{
			tarjan(nxt);
			low[now]=min(low[now],low[nxt]);
		}
		else	if(insta[nxt]&&dfn[nxt]<low[now])
			low[now]=dfn[nxt];
	}//缩点的模板
	if(dfn[now]==low[now])
	{
		cnt+=1;
		int tot=0,pas;
		do
		{
			pas=stack[top--];
			belong[pas]=cnt;//记录属于哪一个强连通分量
			num[cnt]+=1;//点数更新
			insta[pas]=false;
			Contain[cnt].push_back(pas);//将一个强连通分量中的点存下来
		}while(pas!=now);
	}
	return ;
}
void build(int now)
{
	for(int i=0;i<Contain[now].size();i++)//遍历强连通分量里的点
	{
		int nxt=Contain[now][i];//取出来
		for(int j=H1[nxt];j;j=L1[j].nxt)//遍历原图
			if(belong[nxt]!=belong[L1[j].point]&&Map[belong[L1[j].point]]!=now)//如果这个强连通分量和这条边所连的分量之间没有建立边,而且是不同的强连通分量
			{
				Map[belong[L1[j].point]]=now;//打一个标记
				add2(belong[nxt],belong[L1[j].point]);//正向存图
				add3(belong[L1[j].point],belong[nxt]);//反向存图
			}
	}
}
void Spfa1()//正图上跑
{
	queue<int>q;
	q.push(belong[1]);
	inque[belong[1]]=true;
	Dis1[belong[1]]=-num[belong[1]];//存负,跑最短路
	while(!q.empty())
	{
		int pas=q.front();q.pop();
		inque[pas]=false;
		for(int i=H2[pas];i;i=L2[i].nxt)
			if(Dis1[L2[i].point]>Dis1[pas]-num[L2[i].point])
			{
				Dis1[L2[i].point]=Dis1[pas]-num[L2[i].point];
				if(!inque[L2[i].point]);
				{
					inque[L2[i].point]=true;
					q.push((L2[i].point));
				}
			}
	}
}
void Spfa2()//反图上跑
{
	queue<int>q;
	q.push(belong[1]);inque[belong[1]]=true;
	Dis2[belong[1]]=-num[belong[1]];
	while(!q.empty())
	{
		int pas=q.front();q.pop();
		inque[pas]=false;
		for(int i=H3[pas];i;i=L3[i].nxt)
			if(Dis2[L3[i].point]>Dis2[pas]-num[L3[i].point])
			{
				Dis2[L3[i].point]=Dis2[pas]-num[L3[i].point];
				if(!inque[L3[i].point])
				{
					inque[L3[i].point]=true;
					q.push(L3[i].point);
				}
			}
	}
}
void Star()
{
	for(int i=1;i<=cnt;i++)
		for(int j=H3[i];j;j=L3[j].nxt)
			if(Dis1[L3[j].from]<0&&Dis2[L3[j].point]<0)//枚举
				ans=min(ans,Dis1[L3[j].from]+Dis2[L3[j].point]+num[belong[1]]);
}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	int a,b;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&a,&b);
		add1(a,b);//存图
	}
	for(int i=1;i<=n;i++)
		if(!dfn[i])
			tarjan(i);//tarjan缩点 
	for(int i=1;i<=cnt;i++)
		build(i);//建出缩完点后的图
	Spfa1();//求出从1开始可以到达的点数和
	Spfa2();//求出可以到达1的点数和
	Star();//瞎 star star 枚举
	printf("%d",-ans);
}
原文地址:https://www.cnblogs.com/Lance1ot/p/9255803.html