[LibreOJ β Round #3] 绯色 IOI(抵达)

一、题目

点此看题

二、解法

首先考虑任意两个不同的城市庇护所不同意味着什么。我首先想出来一个 \(\tt naive\) 的结论:每个叶子的庇护所一定是它的父亲,所以有解的条件是每个非叶节点至多连接一个叶子。

要让结论升级才能做题,我们考虑叶节点父亲的庇护所一定是他自己,那么这两个节点的匹配方案是唯一确定的,我们把这两个节点同时删去之后得到一个新树,递归操作直到树为空即可,所以如果有解匹配方案也只有一种。

这种匹配方案是好求的,我们用队列模拟一下删叶子的过程即可。这个唯一的匹配方案就可以把原限制转化成若干个偏序关系,我们把偏序关系建成拓扑图,依次考虑权值 \(1\rightarrow n\) 赋值,维护一个关于编号的小根堆,每次把堆顶删去即可。

三、总结

观察性质,可以从一些简单结论入手,然后再考虑上升结论。

树的匹配问题可以从叶子着手考虑,因为叶子处的匹配可能最简单。

#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
const int M = 500005;
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,cnt,a[M],d[M],ans[M];vector<int> g1[M],g2[M];
void link(int u,int v)
{
	if(u==v) return ;
	g2[u].push_back(v);d[v]++;
}
void tpsort()
{
	priority_queue<int,vector<int>,greater<int> >q;
	for(int i=1;i<=n;i++)
		if(d[i]==0) q.push(i);
	while(!q.empty())
	{
		int u=q.top();q.pop();
		ans[++m]=u;
		for(int i=0;i<g2[u].size();i++)
		{
			int v=g2[u][i];
			d[v]--;
			if(d[v]==0) q.push(v);
		}
	}
	if(m!=n) {puts("-1");return ;}
	for(int i=1;i<=n;i++)
		printf("%d ",ans[i]);
}
signed main()
{
	n=read();
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read();
		g1[u].push_back(v);
		g1[v].push_back(u);
		a[u]++;a[v]++;
	}
	queue<int> q;
	for(int i=1;i<=n;i++)
		if(a[i]==1) q.push(i);
	while(!q.empty())
	{
		int u=q.front(),t=0;q.pop();
		for(int i=0;i<g1[u].size();i++)
			if(a[g1[u][i]]>0)//his shelter
				t=g1[u][i],a[g1[u][i]]=0;
		if(!t) continue;
		//build some edge
		for(int i=0;i<g1[u].size();i++)
			link(t,g1[u][i]);
		for(int i=0;i<g1[t].size();i++)
			link(u,g1[t][i]);
		//update the degree
		for(int i=0;i<g1[t].size();i++)
		{
			a[g1[t][i]]--;
			if(a[g1[t][i]]==1) q.push(g1[t][i]);
		}
		cnt++;
	}
	if(cnt<n/2 || n%2) {puts("-1");return 0;}
	tpsort();
}
posted @ 2021-07-16 09:06  C202044zxy  阅读(145)  评论(0编辑  收藏  举报