CF1120D Power Tree

一、题目

点此看题

二、解法

这种区间加问题通常可以转化为差分问题。

把叶子转化成一个序列,控制每个点对应着控制序列的一段区间,那么就是 l l l加一个数, r + 1 r+1 r+1减一个数,由于最后要变全 0 0 0,而序列的值只能转化而不能消失,所以所有值都会堆到 m + 1 m+1 m+1 m m m是叶子数)那个点去,所以我们要求所有点联通,那么问题就转化成了最小生成树。

输出很恶心,输出集合的某一个子集是最优解,而且要求每个元素都可以会成为最优解。那么相同权值的边不急加进去,放在一起讨论看哪些加进去是合法的。

#include 
#include 
#include 
using namespace std;
const int M = 200005;
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,m,k,t,tot,f[M],a[M],fa[M],sz[M],l[M],r[M];
long long ans;
struct edge
{
	int v,next;
}e[2*M];
struct node
{
	int u,v,c,id;
	bool operator < (const node &b) const
	{
		return c<b.c;
	}
}b[M];
void dfs(int u,int fa)
{
	sz[u]=1;l[u]=1e9;
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(v==fa) continue;
		dfs(v,u);
		sz[u]+=sz[v];
		l[u]=min(l[u],l[v]);
		r[u]=max(r[u],r[v]);
	}
	if(sz[u]==1) l[u]=r[u]=++m;
	b[++k]=node{l[u],r[u]+1,a[u],u};
}
int find(int x)
{
	int tmp=fa[x];
	if(x!=fa[x]) fa[x]=find(fa[x]);
	return fa[x];
}
int main()
{
	n=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read();
		e[++tot]=edge{v,f[u]},f[u]=tot;
		e[++tot]=edge{u,f[v]},f[v]=tot;
	}
	dfs(1,0);
	sort(b+1,b+1+k);
	for(int i=1;i<=m+1;i++) fa[i]=i;
	for(int i=1,j=1;i<=k;i=j)
	{
		for(;b[j].c==b[i].c && j<=k;j++)
		{
			int u=b[j].u,v=b[j].v;
			if(find(u)!=find(v))
				a[++t]=b[j].id;
		}
		for(int k=i;k<j;k++)
		{
			int u=b[k].u,v=b[k].v,c=b[k].c;
			int x=find(u),y=find(v);
			if(x==y) continue;
			fa[x]=y;ans+=c;
		}
	}
	printf("%lld %d\n",ans,t);
	sort(a+1,a+1+t);
	for(int i=1;i<=t;i++)
		printf("%d ",a[i]);
}

你可能感兴趣的:(最小生成树)