缩点+dp bzoj1179 apio抢掠计划

42 篇文章 0 订阅
25 篇文章 0 订阅

好水的题啊好久没做这么水的题了。。。

不过似乎是好久没做题了。。。


用tarjan随便缩下点,然后就成了有向无环图了

这样的话就很好说了

可以用spfa

不过其实随便搞个队列然后dp

或者可以叫bfs一下就可以了。。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<stack>
#include<vector>
#include<queue>
#define pb push_back
#define MAX 500000
#define read(x) scanf("%d",&x)
#define rep(x,y,z) for(int x=y;x<=z;x++)

using namespace std;

int n,m,k,value[MAX],money[MAX],dfn[MAX],pre[MAX],dfs_clock=0,scc_num=0;
int scc[MAX],in[MAX],ans=-0x7777ffff;
int bar[MAX],begin,Begin,f[MAX],can[MAX];

stack<int>s;
vector<int>aim[MAX];
vector<int>graph[MAX];

void tarjan(int x)
{
	dfn[x]=pre[x]=++dfs_clock;
	s.push(x);
	in[x]=1;
	for(int i=0;i<aim[x].size();i++)
	{
		int now=aim[x][i];
		if(!dfn[now])
		{
			tarjan(now);
			pre[x]=min(pre[x],pre[now]);
		}
		else
		{
			if(in[now])
				pre[x]=min(pre[x],dfn[now]);
		}
	}
	if(dfn[x]==pre[x])
	{
		scc_num++;
		while(!s.empty())
		{
			int now=s.top();
			s.pop();
			in[now]=0;
			scc[now]=scc_num;
			if(now==x)
				break;
		}
	}
	return;
}

void make_graph()
{
	rep(i,1,n)
		money[scc[i]]+=value[i];
	rep(i,1,n)
		for(int j=0;j<aim[i].size();j++)
		{
			int now=aim[i][j];
			if(scc[i]!=scc[now])
				graph[scc[i]].pb(scc[now]);
		}
	return;
}

void calc()
{
	memset(f,0,sizeof(f));
	queue<int>q;
	begin=scc[Begin];
	q.push(begin);
	f[begin]=money[begin];
	while(!q.empty())
	{
		int now=q.front();
		q.pop();
		for(int i=0;i<graph[now].size();i++)
		{
			int w=graph[now][i];
			if(f[w]<money[w]+f[now])
			{
				f[w]=money[w]+f[now];
				q.push(w);
			}
		}
	}
	return;
}

void work()
{
	for(int i=1;i<=n;i++)
		if(!dfn[i])
			tarjan(i);

	rep(i,1,k)
		can[scc[bar[i]]]=1;

	make_graph();
	calc();
}

int main()
{
	memset(in,0,sizeof(in));
	memset(dfn,0,sizeof(dfn));
	memset(money,0,sizeof(money));
	memset(can,0,sizeof(can));
	read(n),read(m);
	int x,y;
	rep(i,1,m)
		read(x),read(y),aim[x].pb(y);
	rep(i,1,n)
		read(value[i]);
	read(Begin);
	read(k);
	rep(i,1,k)
		read(bar[i]);
	
	work();

	rep(i,1,scc_num)
		if(can[i])
			ans=max(ans,f[i]);
	printf("%d\n",ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值