其他分享
首页 > 其他分享> > 【题解】【抢掠计划】&&【强联通分量缩点学习笔记】

【题解】【抢掠计划】&&【强联通分量缩点学习笔记】

作者:互联网

P3627 [APIO2009]抢掠计划

Solution:
首先这是一张有向图,点有点权,且给定一个起点,给定多个终点,询问从起点出发,在任意一个终点结束,所经过的点权和最大值

如果对于任意一条边,把它终点的点权作为该边的边权,那么只需从起点出发跑一个最长路就可以了

但问题是,边权都为正,一旦出现环,就无法跑最长路了

注意到一句话:“他可以经过同一路口或道路任意多次。但只要他抢劫过某个 ATM 机后,该 ATM 机里面就不会再有钱了。”,这说明每个点最多只能经过一次。

这道题我们可以用强联通分量和缩点解决

强联通图:对于一个有向图,若任意两个节点都可以互相到达,那么称该图为强连通图

强联通分量:对于一个有向图,它的“极大强联通子图”为强联通分量。一个图可能有多个强联通分量

缩点:对于图的每个强联通分量,在原图中用一个点表示,所形成的新图一定是有向无环图

于是我们就可以求出图的强联通分量,并缩点,再跑一个拓扑排序或spfa,因为没有环,所以可以跑最长路

注意缩点时要把原图中的点的数据集中到缩完后的点上,具体的集中方法根据题目要求灵活处理

Code

#include<bits/stdc++.h>
using namespace std;

//#define int long long
inline int read()
{
	register int x=0,w=1;
	register char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if(ch=='-'){ch=getchar();w=-1;}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();	}
	return x*w;
}
const int N=5e5+100;
int n,m,a[N],s,p,bar[N],low[N],dfn[N],c[N],d[N],barc[N],valc[N],deg[N];
int totc,tot,stk[N],ins[N],top;
vector<int>v[N];
struct node{
	int nxt,to,w;
}e[N];
int head[N],cnt;
void add(int x,int y,int z)
{
	e[++cnt].nxt=head[x];
	e[cnt].to=y;
	e[cnt].w=z;
	head[x]=cnt;
}
void tarjan(int x)//求强联通分量模板
{
	dfn[x]=low[x]=++tot;
	stk[++top]=x;ins[x]=1;
	for(int i=0;i<v[x].size();++i){
		int y=v[x][i];
		if(!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(ins[y]) low[x]=min(low[x],dfn[y]);
	}
	if(low[x]==dfn[x]){
		int y;
		totc++;
		do
		{
			y=stk[top--];
			c[y]=totc;
			ins[y]=0;
		}while(y!=x);
	}
}
void spfa()
{
	queue<int>q;
	int vis[N];
	memset(vis,0,sizeof vis);
	memset(d,0,sizeof d);
	d[c[s]]=valc[c[s]];
	vis[c[s]]=1;
	q.push(c[s]);
	while(q.size())
	{
		int x=q.front();
		q.pop();
		vis[x]=0;
		for(int i=head[x];i;i=e[i].nxt)
		{
			int y=e[i].to;
			if(d[y]<d[x]+e[i].w){
				d[y]=d[x]+e[i].w;
				if(vis[y]) continue;
				q.push(y);
			}
		}
	}
}
void topu()
{
	queue<int>q;
	memset(d,0xcf,sizeof d);
	for(int i=1;i<=totc;++i){
		if(deg[i]==0){
			q.push(i);
			if(c[s]==i) d[i]=valc[i];
		}
	}
	while(q.size()){
		int x=q.front();
		q.pop();
		for(int i=head[x];i;i=e[i].nxt){
			int y=e[i].to;
			deg[y]--;
			if(d[x]>=0) d[y]=max(d[y],d[x]+e[i].w);
			if(deg[y]==0){
				q.push(y);
				if(y==c[s]) d[y]=valc[y];
			}
		}
	}
}
signed main()
{
    n=read();m=read();
    for(int i=1;i<=m;++i){
    	int x,y;
    	x=read();y=read();
    	v[x].push_back(y);
	}
	for(int i=1;i<=n;++i) a[i]=read();
	s=read();p=read();
	for(int i=1;i<=p;++i){
		int x=read();
		bar[x]=1;
	}
	for(int i=1;i<=n;++i){
		if(!dfn[i]) tarjan(i);
	}
	for(int i=1;i<=n;++i){
		barc[c[i]]|=bar[i];
		valc[c[i]]+=a[i];
	}
	for(int i=1;i<=n;++i){
		for(int j=0;j<v[i].size();++j){
			int y=v[i][j];
			if(c[i]==c[y]) continue;
			add(c[i],c[y],valc[c[y]]);
			deg[c[y]]++;
		}		
	}
//	spfa();
    topu();
	int ans=0;
	for(int i=1;i<=totc;++i){
		if(barc[i]){
			ans=max(ans,d[i]);
		}
	}
	cout<<ans;
	return 0;
}


标签:缩点,cnt,ch,联通,int,题解,&&,分量
来源: https://www.cnblogs.com/glq-Blog/p/15527173.html