【BZOJ】1179 [Apio2009]Atm 强连通分量+最短路

80 篇文章 0 订阅
11 篇文章 0 订阅

题目传送门

这题的正解——强连通分量+最短路,还是需要一定的想象力的。

首先,我们把所有能够互相到达的点(也就是强连通分量)进行缩点,缩完后的点的权值就是原先所有点的权值和。

然后,我们对缩完点的图重新建图,和强连通分量相连的点就是和属于这个强连通分量的原先节点相连的点。

最后,我们对新建的图做一遍最短路,然后每次读入一个节点,判断该节点所在的强连通分量到起点s的最短路是否大于当前的ans,若大于,则更新ans。

附上AC代码:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <queue>
#define N 500010
using namespace std;

struct note{
	int from,to,nt;
}s1[N],s2[N];
int n,m,x,y,w[N],num,h1[N],d[N],t[N],sy[N],a[N],sk[N],top,g,size,h2[N],s,q,dis[N],ans;
bool b[N];
queue <int> que;

void read(int& a){
	static char c=getchar();a=0;int f=1;
	while (!isdigit(c)) {if (c=='-') f=-1;c=getchar();}
	while (isdigit(c)) a=a*10+c-'0',c=getchar();
	a*=f;return;
}

void add1(int x,int y){
	s1[num]=(note){x,y,h1[x]};
	h1[x]=num++;
}

void so(int k){
	b[k]=1,t[k]=d[k]=++size,sk[++top]=k;
	for (int i=h1[k]; ~i; i=s1[i].nt)
		if (!d[s1[i].to]) so(s1[i].to),t[k]=min(t[k],t[s1[i].to]);
			else if (b[s1[i].to]) t[k]=min(t[k],d[s1[i].to]);
	if (d[k]==t[k]){
		++g;
		while (1){
			int p=sk[top--];b[p]=0,sy[p]=g,w[g]+=a[p];
			if (p==k) break;
		}
	}
	return;
}

void intt(){
	read(n),read(m),memset(h1,-1,sizeof h1),memset(h2,-1,sizeof h2),num=0;
	for (int i=1; i<=m; ++i) read(x),read(y),add1(x,y);
	for (int i=1; i<=n; ++i) read(a[i]);
	for (int i=1; i<=n; ++i) if (!d[i]) so(i);
}

void add2(int x,int y){
	s2[num]=(note){x,y,h2[x]};
	h2[x]=num++;
}

void build(){
	num=0;
	for (int i=1; i<=n; ++i)
		for (int j=h1[i]; ~j; j=s1[j].nt)
			if (sy[i]!=sy[s1[j].to]) add2(sy[i],sy[s1[j].to]);
}

void spfa(){
	read(s),read(q),s=sy[s];
	memset(dis,0,sizeof dis),memset(b,0,sizeof b);
	que.push(s),b[s]=1,dis[s]=w[s];
	while (!que.empty()){
		int p=que.front();que.pop(),b[p]=0;
		for (int i=h2[p]; ~i; i=s2[i].nt)
			if (dis[s2[i].to]<dis[p]+w[s2[i].to]){
				dis[s2[i].to]=dis[p]+w[s2[i].to];
				if (!b[s2[i].to]){
					b[s2[i].to]=1;
					que.push(s2[i].to);
				}
			}
	}
	while (q--) read(x),x=sy[x],ans=max(ans,dis[x]==0x3f3f3f3f?0:dis[x]);
	printf("%d",ans);
	return;
}

int main(void){
	intt();
	build();
	spfa();
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值