CF611H New Year and Forgotten Tree

一、题目

点此看题

二、解法

看来什么东西反向构造都难

首先不难想到把点按位数染色,一个显然的 \(\tt observation\) 是:每种颜色的点等价。

考虑适当枚举简化问题,可以在每个颜色中选出一个代表点,然后把这些代表点做生成树,剩下的点接在这些代表点上,这是因为如果存在解,那么就存在其他点之连接代表点的解。可以用调整法简单证明,如果其他点接在非关键点上,那么调整到关键点上依然合法。

反正颜色数很小,可以枚举 \(\tt prufer\) 序列来确定关键点的生成树。然后把每种颜色的点放在一起考虑,边 \((i,j)\) 可以通过和颜色 \(j\) 关键点的连边解决一个颜色 \(i\) 中的点,也可以通过和颜色 \(i\) 关键点连边解决一个颜色 \(j\) 中的点。

你发现这就是一个二分图匹配模型,把边建在 \(X\) 部,把颜色建在 \(Y\) 部,跑出残量网络即可构造出解。

三、总结

本题直接入手很不好做,需要通过适当枚举简化问题。

最关键的地方其实是选出关键点,得到这个思路需要你对树形结构有一定的理解,树可以通过先构造基本树在把其他点接在这个树上完成,因为每种颜色的点等价才想到用颜色来构建基本树。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int M = 105;
const int inf = 0x3f3f3f3f;
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,b[8][8],num[8],p[8],a[8];
//the part of dinic
int cnt,tot,S,T,f[M],dis[M];
struct edge
{
	int v,c,next;
}e[M*M];
void add(int u,int v,int c)
{
	e[++tot]=edge{v,c,f[u]},f[u]=tot;
	e[++tot]=edge{u,0,f[v]},f[v]=tot;
}
int bfs()
{
	queue<int> q;
	memset(dis,0,sizeof dis);
	dis[S]=1;q.push(S);
	while(!q.empty())
	{
		int u=q.front();q.pop();
		if(u==T) return 1;
		for(int i=f[u];i;i=e[i].next)
		{
			int v=e[i].v;
			if(!dis[v] && e[i].c>0)
			{
				dis[v]=dis[u]+1;
				q.push(v);
			}
		}
	}
	return 0;
}
int dfs(int u,int ept)
{
	if(u==T) return ept;
	int flow=0,tmp=0;
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(dis[v]==dis[u]+1 && e[i].c>0)
		{
			tmp=dfs(v,min(ept,e[i].c));
			e[i].c-=tmp;
			e[i^1].c+=tmp;
			flow+=tmp;
			ept-=tmp;
			if(!ept) break;
		}
	}
	return flow;
}
int dinic()
{
	int ans=0;
	while(bfs()) ans+=dfs(S,inf);
	return ans;
}
//check the tree
void check()
{
	tot=1;cnt=S=0;
	for(int i=0;i<M;i++) f[i]=0;
	//use prufer to build tree
	int q[8]={},c[8][8]={},d[8]={}
	,vis[8]={},id[8][8]={};
	a[m-1]=m;
	for(int i=1;i<m;i++) d[a[i]]++;
	for(int i=1;i<m;i++)
	{
		int p=0;
		for(int j=1;j<=m;j++)
			if(!vis[j] && !d[j])
				p=j;
		vis[p]=1;d[a[i]]--;
		c[min(p,a[i])][max(p,a[i])]++;
	}
	for(int i=1;i<=m;i++)
		for(int j=i;j<=m;j++)
			id[i][j]=++cnt;
	T=cnt+1+m;
	for(int i=1;i<=m;i++)
		for(int j=i;j<=m;j++)
		{
			if(c[i][j]>b[i][j]) return ;
			add(S,id[i][j],b[i][j]-c[i][j]);
			add(id[i][j],cnt+i,inf);
			add(id[i][j],cnt+j,inf);
		}
	for(int i=1;i<=m;i++)
		add(cnt+i,T,num[i]-1);
	if(dinic()<n-m) return ;
	for(int i=1;i<=m;i++) q[i]=p[i];
	for(int i=1;i<=m;i++)
		for(int j=1;j<=m;j++) if(c[i][j])
			printf("%d %d\n",p[i],p[j]);
	for(int i=1;i<=m;i++)
		for(int j=i;j<=m;j++)
		{
			int u=id[i][j];
			for(int w=f[u];w;w=e[w].next)
			{
				int v=e[w].v,c=e[w^1].c;
				if(v==S) continue;
				if(v-cnt==i) while(c--)
					printf("%d %d\n",++q[i],p[j]);
				else while(c--)
					printf("%d %d\n",++q[j],p[i]);
			}
		}
	exit(0);
}
//fuck all prufer
void zxy(int x)
{
	if(x==m-1)
	{
		check();
		return ;
	}
	for(int i=1;i<=m;i++)
	{
		a[x]=i;
		zxy(x+1);
	}
}
signed main()
{
	n=read();
	for(int i=1;i<n;i++)
	{
		char s[10],t[10];
		scanf("%s%s",s,t);
		int l1=strlen(s),l2=strlen(t);
		b[min(l1,l2)][max(l1,l2)]++;
	}
	for(int i=1;i<=n;i*=10) p[++m]=i;
	for(int i=1;i<m;i++) num[i]=p[i+1]-p[i];
	num[m]=n-p[m]+1;
	if(m==1)
	{
		if(b[1][1]!=n-1) puts("-1");
		else
		{
			for(int i=1;i<n;i++)
				printf("%d %d\n",i,i+1);
		}
		return 0;
	}
	zxy(1);
	puts("-1");
}
posted @ 2021-09-03 20:37  C202044zxy  阅读(78)  评论(0编辑  收藏  举报