[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();
}