【Codeforces 611H】New Year and Forgotten Tree
搜索,最大流。
题目大意:
给定一棵 $n$ 个点的树,告诉你每条边连接的两个点的编号的位数分别是多少,要求还原这棵树。输出任意方案。
题解:
位数相同的点之间是没有区别的,我们放在一起考虑。则最多有 $6$ 种不同位数。
对于相同位数的点,它们之间的连边,相当于把两个点缩成了一个点,也是没有区别的,所以可以任意连,每个连通块保留一个点和其他点相连即可。
考虑把每个不同的位数都取出一个结点作为主要结点,其他结点只作为叶子连在主要结点上。由于原图是一棵树,所以主要结点之间的连边形成一棵树。
由于不同连边方式很少,所以我们可以枚举这棵树的形态,然后考虑把剩下的结点连上去。
我们记录 $e[u][v]$ 表示位数为 $u$ 和 $v$ 的点之间还差多少条边。有两种方式连边:取出位数为 $u$ 的点,连向位数为 $v$ 的主要结点;或者反之。
相当于每个位数有一些点,需要去匹配它要连的点,以满足连边的需求。
我们可以用最大流来完成这个匹配的过程。当满流的时候,说明有合法方案。
输出方案在最大流的残量网络上不难构造。
细节较多。
Code:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=114;
int n,len,LST[8];
queue<int>q;
int eu[20],ev[20],cnt,fa[8],t0t[8],es[8][8],dep[N];
int id[2][33];
vector<int>num[8];
inline int find(int x){while(x!=fa[x])x=fa[x]=fa[fa[x]];return x;}
struct edge{
int to,nxt,cap;
}e[N<<1];
int head[N],cnT,T,iter[N];
inline int addedge(int u,int v,int cap){
e[++cnT]=(edge){v,head[u],cap},head[u]=cnT;
e[++cnT]=(edge){u,head[v],0},head[v]=cnT;
return cnT;
}
bool chk(int zt){
for(int i=1;i<=len;++i)fa[i]=i;
for(int i=0;i<cnt;++i)
if(zt>>i&1){
int x=find(eu[i]),y=find(ev[i]);
if(x==y)return 0;
fa[y]=x;
}
return 1;
}
bool bfs(){
memset(dep,0,sizeof dep);
*dep=1;
for(q.push(0);!q.empty();){
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].nxt)
if(e[i].cap&&!dep[e[i].to]){
dep[e[i].to]=dep[u]+1;
q.push(e[i].to);
}
}
return dep[T];
}
int dfs(int u,int f){
if(u==T||!f)return f;
for(int&i=iter[u];i;i=e[i].nxt)
if(e[i].cap&&dep[e[i].to]>dep[u]){
int d=dfs(e[i].to,min(e[i].cap,f));
if(d){
e[i].cap-=d,e[i^1].cap+=d;
return d;
}
}
return 0;
}
void output(int S){
for(int i=1;i<=len;++i){
vector<int>&vec=num[i];
for(int j=1;j<=es[i][i];++j){
int a=vec.back();vec.pop_back();
int b=vec.back();
printf("%d %d\n",a,b);
}
LST[i]=vec.back();
vec.pop_back();
}
for(int i=0;i<cnt;++i)if(S>>i&1)
printf("%d %d\n",LST[eu[i]],LST[ev[i]]);
for(int i=0;i<cnt;++i){
int x=id[0][i],u=eu[i],v=ev[i];
for(int k=e[x].cap;k;--k){
int a=num[u].back();
num[u].pop_back();
printf("%d %d\n",a,LST[v]);
}
x=id[1][i],u=ev[i],v=eu[i];
for(int k=e[x].cap;k;--k){
int a=num[u].back();
num[u].pop_back();
printf("%d %d\n",a,LST[v]);
}
}
}
int main(){
scanf("%d",&n);
len=log10(n)+1;
for(int i=1;i<n;++i){
char a[8],b[8];
scanf("%s%s",a,b);
int x=strlen(a),y=strlen(b);
if(x>y)swap(x,y);
++es[x][y];
}
for(int i=1;i<=n;++i)
if(i<10)++t0t[1],num[1].push_back(i);else
if(i<100)++t0t[2],num[2].push_back(i);else
if(i<1000)++t0t[3],num[3].push_back(i);else
if(i<10000)++t0t[4],num[4].push_back(i);else
if(i<100000)++t0t[5],num[5].push_back(i);else++t0t[6],num[6].push_back(i);
for(int i=1;i<=len;++i)if((t0t[i]-=es[i][i]+1)<0){
puts("-1");
return 0;
}
for(int i=1;i<=len;++i)
for(int j=i+1;j<=len;++j)
eu[cnt]=i,ev[cnt++]=j;
int sm=0;
for(int i=1;i<=len;++i)sm+=t0t[i];
for(int S=0;S<1<<cnt;++S)
if(__builtin_popcount(S)==len-1&&chk(S)){
for(int i=0;i<cnt;++i)
if(S>>i&1)--es[eu[i]][ev[i]];
bool ok=1;
for(int i=0;i<cnt;++i)
if(es[eu[i]][ev[i]]<0)ok=0;
if(!ok){
for(int i=0;i<cnt;++i)
if(S>>i&1)++es[eu[i]][ev[i]];
continue;
}
cnT=1;
memset(head,0,sizeof head);
T=len+cnt+1;
for(int i=1;i<=len;++i)
addedge(0,i,t0t[i]);
for(int i=0;i<cnt;++i){
int u=eu[i],v=ev[i];
id[0][i]=addedge(u,i+1+len,n);
id[1][i]=addedge(v,i+1+len,n);
addedge(i+1+len,T,es[eu[i]][ev[i]]);
}
int flow=0;
while(bfs()){
memcpy(iter,head,sizeof iter);
while(int f=dfs(0,1919810))flow+=f;
}
for(int i=0;i<cnt;++i)
if(S>>i&1)++es[eu[i]][ev[i]];
if(flow!=sm)continue;
output(S);
return 0;
}
puts("-1");
return 0;
}