codeforces1388D. Captain Flint and Treasure

https://codeforces.com/contest/1388/problem/D

拓扑排序,如果a[u]>0,说明先拿a[u]再拿a[v]会大一些,如果a[u]<0,那么就先把v所在的集合拿完,再拿u所在的集合,才能让a[u]不使得a[v]变小

所以先拓扑排序顺便用并查集合并,然后把每一个集合里的元素输出顺序先拓扑排序存起来,然后把拓扑排序过程中的反向边连起来,得到集合的输出顺序。

其实有更短一些的写法就是直接建反向边,然后dfs从底向上反向转移求得答案,这样输出方案十分方便

upd

看了jiangly的代码,我傻逼了,直接正序拓扑排序的时候正序就建正边,不能就建反边,不一定要一整个集合顺序都满足,只要u->v当a[u]<0时变成v->u的边就行了,也就是v在u之前任意时刻出现都行。后面是辣鸡代码

#include
#define pb push_back
using namespace std;
typedef long long ll;

const int maxl=3e5+10;

int n,m,cas,k,cnt,tot;
ll ans;
int b[maxl],du[maxl];
ll a[maxl];
char s[maxl];
bool in[maxl]; 
vector e[maxl];
struct lne
{
	int u,v;
}fed[maxl];
queue q;

inline void prework()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&b[i]);
		if(b[i]!=-1)
			du[b[i]]++;
	}
} 

inline void mainwork()
{
	
	for(int i=1;i<=n;i++)
	if(du[i]==0)
		q.push(i);
	int u,v,x,y;ans=0;
	while(!q.empty())
	{
		u=q.front();q.pop();
		ans+=a[u];v=b[u];
		if(v==-1) continue;
		if(a[u]>=0)
		{
			e[u].push_back(v); 
			a[v]+=a[u];
		}
		else
			e[v].push_back(u);
		if(--du[v]==0)
			q.push(v);
	}
}

inline void print()
{
	printf("%lld\n",ans);
	for(int i=1;i<=n;i++)
		du[i]=0;
	for(int i=1;i<=n;i++)
		for(int v:e[i])
			du[v]++;
	for(int i=1;i<=n;i++)
	if(du[i]==0)
		q.push(i);
	int u,v;
	while(!q.empty())
	{
		u=q.front();q.pop();
		printf("%d ",u);
		for(int v:e[u])
		if(--du[v]==0)
			q.push(v);
	}
}

int main()
{
	int t=1;
	//scanf("%d",&t);
	for(cas=1;cas<=t;cas++)
	{
		prework();
		mainwork();
		print();
	}
	return 0;
}
#include
#define pb push_back
using namespace std;
typedef long long ll;

const int maxl=3e5+10;

int n,m,cas,k,cnt,tot;
ll ans;
int b[maxl],du[maxl],c[maxl],fdu[maxl],f[maxl];
ll a[maxl];
char s[maxl];
bool in[maxl]; 
vector e[maxl],g[maxl],fe[maxl],out[maxl];
vector nod;
struct lne
{
	int u,v;
}fed[maxl];
queue q;

inline void prework()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&b[i]);
		f[i]=i;
		if(b[i]!=-1)
			du[b[i]]++;
	}
} 

inline int find(int x)
{
	if(f[x]!=x)
		f[x]=find(f[x]);
	return f[x];
}

inline void mainwork()
{
	
	for(int i=1;i<=n;i++)
	if(du[i]==0)
		q.push(i);
	int u,v,x,y;ans=0;
	while(!q.empty())
	{
		u=q.front();q.pop();
		ans+=a[u];
		v=b[u];
		if(v==-1) continue;
		if(a[u]>=0)
		{
			a[v]+=a[u];
			x=find(u);y=find(v);
			f[x]=y;
		}
		else
			fed[++tot]=lne{v,u};
		du[v]--;
		if(!du[v])
			q.push(v);
	}
	for(int i=1;i<=n;i++)
		g[find(i)].push_back(i);
	for(int i=1;i<=n;i++)
	if(find(i)==i)
	{
		nod.push_back(i);
		for(int d:g[i])
			du[i]=0;
		for(int d:g[i])
		if(b[d]!=-1 && find(b[d])==i)
			du[b[d]]++;
		for(int d:g[i])
		if(du[d]==0)
			q.push(d);
		while(!q.empty())
		{
			u=q.front();q.pop();
			out[i].push_back(u);
			v=b[u];
			if(b[u]==-1 || find(v)!=i)
				continue;
			du[v]--;
			if(!du[v])
				q.push(v);
		}
			
	}
}

inline void print()
{
	printf("%lld\n",ans);
	for(int d:nod)
		du[d]=0;
	for(int i=1;i<=tot;i++)
	{	
		fe[find(fed[i].u)].push_back(find(fed[i].v));
		du[find(fed[i].v)]++;
	}
	for(int d:nod)
	if(du[d]==0)
		q.push(d);
	int u,v;
	while(!q.empty())
	{
		u=q.front();q.pop();
		for(int x:out[u])
			printf("%d ",x);
		for(int v:fe[u])
		{
			--du[v];
			if(du[v]==0)
				q.push(v);
		}
	}
}

int main()
{
	int t=1;
	//scanf("%d",&t);
	for(cas=1;cas<=t;cas++)
	{
		prework();
		mainwork();
		print();
	}
	return 0;
}

 

你可能感兴趣的:(拓扑排序)