[JOISC 2016 Day3] 电报

一、题目

点此看题

二、解法

最后形成的强联通分量肯定是一个环,每个点的出度一直为 \(1\),那么我们只要让入度也都为 \(1\) 即可。

每个点保留权值最大的入边,其他入边贪心断开即可。

但是这样还有问题,操作过后可能会形成若干个环,我们要把这些小环接成一个大环。那么一个环上至少满足一个点他断开了环边,所以我们处理出非环边的最大权值,然后贪心地看替换环上哪个点最好即可。

注意特判整张图是一个大环的情况,时间复杂度 \(O(n)\)

三、总结

这个贪心实在是太简洁了,我觉得它的本质是拆分,把最后是一个环这种大限制拆成了每个点入度为 \(1\) 的小限制,只考虑单点是非常容易的(我觉得单点限制优于整体限制),再考虑例外来修正我们的限制转化即可。

\(\tt UPD 2021/11/18\):我觉得这道题就是构造答案下界,你会发现每一步都是必要的。

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 100005;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,ans,a[M],c[M],mx[M],cl[M],vis[M];
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		a[i]=read();c[i]=read();
		ans+=c[i];
	}
	for(int i=1;i<=n;i++)
		if(vis[i]==0)
		{
			int x=0,y=0,cnt=0;
			for(x=i;vis[x]==0;x=a[x]) vis[x]=i;
			if(vis[x]==i)//have this kind of color
			{
				for(y=x;vis[y]!=-1;y=a[y])
					vis[y]=-1,cnt++;//cycle tag
			}
			if(cnt==n) {puts("0");return 0;}
		}
	for(int i=1;i<=n;i++)
	{
		mx[a[i]]=max(mx[a[i]],c[i]);
		if(vis[i]!=-1) cl[a[i]]=max(cl[a[i]],c[i]);
	}
	for(int i=1;i<=n;i++)
		ans-=mx[i];
	for(int i=1;i<=n;i++)
		if(vis[i]==-1)
		{
			int mi=1e9;
			for(int x=i;vis[x]==-1;x=a[x])
				mi=min(mi,mx[x]-cl[x]),vis[x]=0;
			ans+=mi;
		}
	printf("%lld\n",ans);
}
posted @ 2021-07-15 21:45  C202044zxy  阅读(401)  评论(0编辑  收藏  举报