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");
}