「PMOI-4」可怜的团主

一、题目

点此看题

这道题考试时候打了缩点,然后一无所获,虽然想出了那个超级神奇的构造方法。

还是不要思维定式啊,我以为难的图论题一定要缩点,但是我从来一打缩点就爆炸

二、解法

比较传统的树上二选一构造问题,根据套路任何情况一定有解

直接考虑 \(\tt dfs\) 树,叶子之间一定无边,如果有 \(\geq\lfloor\frac{n}{3}\rfloor\) 个叶子(不考虑根),那么我们找到解。

那么有叶子数量 \(<\lfloor\frac{n}{3}\rfloor\),考虑根是叶子也只有至多 \(\lfloor\frac{n}{3}\rfloor\) 个点,那么我们把它们两两匹配得到路径至多有 \(\lceil\frac{n}{6}\rceil\) 条。

现在问题转化成了构造一个叶子的匹配,使得树上所有点都被覆盖。我的方法:把所有叶子按 \(\tt dfs\) 序排序,设一共有 \(k\) 个叶子(如果 \(k\) 是奇数那么我们补上根),然后 \(i\)\(i+\frac{k}{2}\) 匹配。

已经过了,跪求上面叶子匹配构造方法的正确性证明。
UPD:有一个巨佬在我的博客下面评论了证明,我觉得他的证明是对的。

#include <cstdio>
#include <algorithm>
using namespace std;
const int M = 1005;
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,tot,Ind,cnt,f[M],dfn[M];
int fa[M],id[M],dep[M],a[M],b[M];
struct edge
{
	int v,next;
}e[M*M];
void dfs(int u)
{
	dfn[u]=++Ind;int sz=0;
	dep[u]=dep[fa[u]]+1;
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(dfn[v]) continue;
		fa[v]=u;dfs(v);
		sz++;
	}
	if(sz==0 || (sz==1 && u==1))
		id[++k]=u;
}
int cmp(int x,int y)
{
	return dfn[x]<dfn[y];
}
void match(int u,int v)
{
	int t1=0,t2=0;cnt++;
	while(u!=v)
	{
		if(dep[u]>dep[v])
			a[++t1]=u,u=fa[u];
		else
			b[++t2]=v,v=fa[v];
	}
	a[++t1]=u;
	printf("%d ",t1+t2);
	for(int i=1;i<=t1;i++)
		printf("%d ",a[i]);
	for(int i=t2;i>=1;i--)
		printf("%d ",b[i]);
	puts("");
}
signed main()
{
	n=read();m=read();
	for(int i=1;i<=m;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);int fl=0;
	if(id[k]==1) k--,fl=1;
	if(k>=n/3)
	{
		puts("2");
		for(int i=1;i<=n/3;i++)
			printf("%d ",id[i]);
		puts("");
		return 0;
	}
	if(fl==1) id[++k]=1;
	if(k%2) id[++k]=1;
	sort(id+1,id+1+k,cmp);
	puts("1");
	for(int i=1;i<=k/2;i++)
		match(id[i],id[i+k/2]);
	for(int i=2;i<=n;i++)
		if(cnt<(n+5)/6)
		{
			printf("%d %d\n",1,i);
			cnt++;
		}
}
posted @ 2021-08-20 21:17  C202044zxy  阅读(375)  评论(1编辑  收藏  举报