bzoj1179: [Apio2009]Atm(强联通+最短路)

题目传送门


题意大意:

 

在有向图里,可以绕圈的情况下,取尽可能大的值。然后要求在有酒吧的点里尽可能大。

可以绕圈:能否想到联通块

尽可能大的值:应该能想到最短路吧

有酒吧的点里,尽可能大:扫一遍(水)。。。。


思路分析:

1、跑强连通;

2、以团块作为点,重新构图;

3、对图跑一次spfa;

4、扫一遍有酒吧的点,取最大的数。


上代码:

(这题是我第一次尝试打缩点的代码,点和边的结构体都有重复应用,代码里有详细注解。)


#include<cstdio>

const int mx=500005;
int n,m,s,p,len=0,lb=0,lx=0;
int h[mx],bb[mx],l[mx],tou=0,wei;

struct nod{int v,i,d,b;}a[mx];
struct nod1{int x,y,gg;}b[mx];

void ins(int x,int y)
{
	len++; b[len].x=x; b[len].y=y; b[len].gg=h[x]; h[x]=len;
}

void dfs(int x)//强连通的模板
{
	a[x].i=a[x].d=++lx; a[x].v=1; l[++tou]=x; 
	for(int i=h[x];i>0;i=b[i].gg)
	{
		int y=b[i].y;
		if(a[y].i==0)
		{
			dfs(y); if(a[y].d<a[x].d) a[x].d=a[y].d;
		}
		else if(a[y].v==1)
		{
			if(a[y].i<a[x].d) a[x].d=a[y].i;
		}
	}
	if(a[x].i==a[x].d)
	{
		lb++; int k;
		while(1)
		{
			k=l[tou--];
			a[k].v=0; a[k].b=lb;  
			if(k==x) break;
		}
	}
}
void spfa()//最短路的模板(以团块来跑)
{	
	for(int i=1;i<=lb;i++) a[i].v=a[i].d=0;//现在以团块为点 
	tou=1;wei=2;l[1]=s;
	a[s].d=bb[s];a[s].v=1;
	while(tou!=wei)
	{
		int x=l[tou];
		for(int i=h[x];i>0;i=b[i].gg)
		{
			int y=b[i].y; 
			if(a[y].d<a[x].d+bb[y])
			{
				a[y].d=a[x].d+bb[y];
				if(a[y].v==0)
				{
					a[y].v=1;l[wei++]=y; if(wei>lb) wei=1;
				}
			}
		}a[x].v=0; tou++;if(tou>lb) tou=1;
	}
}
int maxx(int x,int y) { return x>y?x:y; }
int main()
{
	scanf("%d %d",&n,&m); int x,y;
	for(int i=1;i<=n;i++) bb[i]=a[i].v=a[i].i=a[i].d=h[i]=0;
	for(int i=1;i<=m;i++) { scanf("%d %d",&x,&y); ins(x,y); }
	for(int i=1;i<=n;i++) { if(a[i].i==0) dfs(i); }//跑强连通
	for(int i=1;i<=n;i++) { scanf("%d",&x); bb[a[i].b]+=x; }//统计每个团的金币总量 
	scanf("%d %d",&s,&p);
	
	//缩点:重新构图(以团为点,建边)
	int ll=len; len=0; for(int i=1;i<=n;i++) h[i]=0;
	for(int i=1;i<=ll;i++)//扫描所有的边,如果是联通块之间的 ,就重构 
	{					  //以团体作为 a 数组了 
		x=b[i].x; y=b[i].y;
		if(a[x].b!=a[y].b) ins(a[x].b,a[y].b);//团体之间连边 
	} 
	
	//到此为止,h,b数组只对团体服务;
	//a数组的 b 对点服务; 
	//a数组的 v、d 对团体服务;(i参数没用了) 
	s=a[s].b;//s点,现在已经是一个团体
	spfa();//跑个最短路 :求出各个团体的最优解
	
	int ans=0;
	for(int i=1;i<=p;i++)//扫团体,看看能否找到酒吧 
	{
		scanf("%d",&x);
		ans=maxx(ans, a[a[x].b].d);
	} 
	printf("%d\n",ans);
	return 0;
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值