【APIO2009】抢掠计划(有向图强连通分量+缩点+DAG图上的dp)

(题目描述请参照原题....)


分析:

1.首先可以看出这题要求强连通分量,因为进入了一个分量就可以把钱取光然后在任意一个点出去;

2.因为最后要到一个酒吧,所以在求分量的同时,既要计算这个分量的钱,又要判断有没有酒吧;

3.求完分量进行缩点:每个点的信息包括钱和有无酒吧;

4.利用缩的点进行DAG图上的dp,最后在有酒吧的点中选择总钱数最大的即可


代码:

#include
#include
#include 
using namespace std;
const int maxn=500005;
const int maxm=500005;
const int INF=-205000000;
int n,m,S,P,np=0,np2=0,last[maxn],last2[maxn],monny[maxn],bar[maxn];
struct edge{int to,pre;}E[maxm],E2[maxm];

char c;
void qkscanf(int &x)
{
	for(c=getchar();c<'0'||c>'9';c=getchar());
	for(x=0;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
}

void addedge(int u,int v)
{
	E[++np]=(edge){v,last[u]};
	last[u]=np;
}

void addedge2(int u,int v)
{
	E2[++np2]=(edge){v,last2[u]};
	last2[u]=np2;
}

int dfn[maxn],low[maxn],stk[maxn],belong[maxn],have_bar[maxn],size[maxn],top=0,dfs_clock=0,scc_cnt=0;
void DFS(int i)
{
	dfn[i]=low[i]=++dfs_clock;
	stk[++top]=i;
	for(int p=last[i];p;p=E[p].pre)
	{
		int j=E[p].to;
		if(dfn[j])
		{
			if(!belong[j]) low[i]=min(low[i],low[j]);
			continue;
		}
		
		DFS(j);
		low[i]=min(low[i],low[j]);
	}
	
	if(dfn[i]==low[i])
	{
		scc_cnt++;
		while(1)
		{
			int x=stk[top--];
			if(bar[x]) have_bar[scc_cnt]=1;
			belong[x]=scc_cnt;
			size[scc_cnt]+=monny[x];
			if(x==i) break;
		} 
	}
}

void suodian()
{
	for(int i=1;i<=n;i++)
	{
		for(int p=last[i];p;p=E[p].pre)
		{
			int j=E[p].to;
			if(belong[i]!=belong[j]) addedge2(belong[j],belong[i]);
		}
	}
}

int d[maxn];
int dp(int i)
{
	if(d[i]!=-1) return d[i];
	int t;
	if(i==belong[S]) t=0;
	else t=INF;
	for(int p=last2[i];p;p=E2[p].pre)
	{
		int j=E2[p].to;
		t=max(t,dp(j));
	}
	t+=size[i];
	return d[i]=t;
}

int main()
{
//	freopen("in.txt","r",stdin);
	qkscanf(n);qkscanf(m);
	int u,v;
	for(int i=1;i<=m;i++)
	{
		qkscanf(u);qkscanf(v);
		addedge(u,v);
	}
	
	for(int i=1;i<=n;i++) qkscanf(monny[i]);
	
	qkscanf(S);qkscanf(P);
	int x;
	for(int i=1;i<=P;i++) 
	{
		qkscanf(x);
		bar[x]=1;
	}
	
	for(int i=1;i<=n;i++) if(!dfn[i]) DFS(i);//求分量 
	
	suodian();//缩点 
	
	memset(d,-1,sizeof(d));//做DAG上的dp 
	int ans=0;
	for(int i=1;i<=scc_cnt;i++) 
	{
		int t=dp(i);
		if(have_bar[i]) ans=max(ans,t);
	}
	
	printf("%d",ans);
	
	return 0;
}

你可能感兴趣的:(图论-强连通分量)