题意大意:
在有向图里,可以绕圈的情况下,取尽可能大的值。然后要求在有酒吧的点里尽可能大。
可以绕圈:能否想到联通块
尽可能大的值:应该能想到最短路吧
有酒吧的点里,尽可能大:扫一遍(水)。。。。
思路分析:
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;
}