「APIO 2009」抢掠计划

传送门


problem

给定一个 n n n 个点 m m m 条边的有向图,点带权,图中有若干个点是特殊点。

从给定的起点出发,以任意的特殊点为终点,求出路径上点权和的最大值(如果重复走过只算一次)。

数据范围: n , m ≤ 5 × 1 0 5 n, m\le5\times10^5 n,m5×105


solution

Tarjan 缩完点后,拓扑排序时 d p dp dp 求最大点权和即可。注意一些细节问题。

时间复杂度 O ( n ) O(n) O(n)


code

#include<bits/stdc++.h>
using namespace std;
namespace IO{
	const int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	template<typename T>
	inline T Read(){
		char c=gc();T x=0,f=1;
		while(!isdigit(c))  f^=(c=='-'),c=gc();
		while( isdigit(c))  x=((x+(x<<2))<<1)+(c^48),c=gc();
		return f?x:-x;
	}
	inline int in()  {return Read<int>();}
}
using IO::in;
const int N=5e5+5;
int n,m,S,p,v[N],key[N];
struct edge{
	int t,first[N],v[N],nxt[N];
	void add(int x,int y)  {nxt[++t]=first[x],first[x]=t,v[t]=y;}
}G,DAG;
struct edges{int u,v;}e[N];
int tot,top,scc,dfn[N],low[N],stk[N],instk[N],bel[N],val[N],flag[N];
void Max(int &x,int y)  {if(x<y)x=y;}
void Min(int &x,int y)  {if(x>y)x=y;}
void Tarjan(int x){
	dfn[x]=low[x]=++tot;
	stk[++top]=x,instk[x]=1;
	for(int i=G.first[x];i;i=G.nxt[i]){
		int to=G.v[i];
		if(!dfn[to])  Tarjan(to),Min(low[x],low[to]);
		else  if(instk[to])  Min(low[x],dfn[to]);
	}
	int k;
	if(dfn[x]==low[x]){
		scc++;
		do{
			k=stk[top--],instk[k]=0;
			bel[k]=scc,val[scc]+=v[k],flag[scc]|=key[k];
		}while(k!=x);
	}
}
queue<int>Q;
int ans,deg[N],f[N];
void Topsort(){
	ans=val[bel[S]]*flag[bel[S]];
	Q.push(bel[S]),f[bel[S]]=val[bel[S]];
	while(!Q.empty()){
		int x=Q.front();Q.pop();
		for(int i=DAG.first[x];i;i=DAG.nxt[i]){
			int to=DAG.v[i];
			Max(f[to],f[x]+val[to]),Max(ans,f[to]*flag[to]);
			if(!(--deg[to]))  Q.push(to);
		}
	}
}
int main(){
	n=in(),m=in();
	for(int i=1;i<=m;++i){
		e[i].u=in(),e[i].v=in(),G.add(e[i].u,e[i].v);
	}
	for(int i=1;i<=n;++i)  v[i]=in();
	S=in(),p=in();
	while(p--)  key[in()]=1;
	Tarjan(S);
	for(int i=1;i<=m;++i){
		int u=e[i].u,v=e[i].v;
		if(bel[u]&&bel[v]&&bel[u]!=bel[v])  DAG.add(bel[u],bel[v]),++deg[bel[v]];
	}
	Topsort();
	printf("%d\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值