题意
给定你一棵树,结点编号为 (1-n) 现在用问号的个数代表每个节点编号那个数字的长度,请你还原这一颗树,任意输出一个方案,可能无解。
传送门
(n<2 imes 10^5)
思路
首先位数相同的点之间的边随便连。
因此剩下的就是一些不同色的点,要求 (a[i][j]=x) ,求连边方案(同色间不能连)。
如果有解则一定存在一种方案,是将每种位数拿一个点出来,连成一个生成树,然后把其它的点连到生成树上的点。
爆搜生成树的形态,然后用网络流求匹配判断是否存在方案。即对于 (a[i][j]) 可能是小 (i) 连向生成树中的 (j) 色或者反过来,两种加起来刚好满足时,对于两种情况的分配。
输出答案(注意细节)
#include <bits/stdc++.h>
using std::queue;
const int INF=200000,N=100;
int n,a[100],f[100][100],w,fa[100],m,b[100],tot,s,t,last[N],to[N<<2],Next[N<<2],edge,flow[N<<2];
int deep[N],sum,c[N];
queue <int> q;
char s1[100],s2[110];
struct ed{
int x,y;
}e[100];
int cw(int x){
int cnt=0;
while (x) x=x/10,cnt++;
return cnt;
}
int find(int x){
if (fa[x]==x) return x;
fa[x]=find(fa[x]);
return fa[x];
}
bool p(){//判断是否是树
for (int i=1;i<=w;i++) fa[i]=i;
for (int i=1;i<=m;i++){
if (b[i]==1){
int fx=find(e[i].x),fy=find(e[i].y);
if (fx==fy) return false;
fa[fx]=fy;
}
}
return true;
}
int cal(int x){return x&1?x+1:x-1;}
void add(int x,int y,int z){
to[++edge]=y;
Next[edge]=last[x];
last[x]=edge;
flow[edge]=z;
}
void Add(int x,int y,int z){add(x,y,z),add(y,x,0);}
bool bfs(){
memset(deep,0,sizeof(deep));
deep[s]=1;
q.push(s);
while (!q.empty()){
int x=q.front();
q.pop();
for (int i=last[x];i;i=Next[i])
if (flow[i] && !deep[to[i]]){
deep[to[i]]=deep[x]+1;
q.push(to[i]);
}
}
return deep[t];
}
int Dfs(int x,int now){
if (x==t) return now;
for (int i=last[x];i;i=Next[i]){
if (deep[to[i]]<=deep[x]) continue;
if (!flow[i]) continue;
int di=Dfs(to[i],std::min(now,flow[i]));
if (di){
flow[i]-=di;
flow[cal(i)]+=di;
return di;
}
}
return 0;
}
void Printf(){
for (int i=1;i<=m;i++)
if (b[i]) printf("%d %d
",c[e[i].x],c[e[i].y]);//主生成树
for (int i=1;i<=w;i++)//同位数
for (int j=c[i+1]-1;j>=c[i+1]-f[i][i];j--) printf("%d %d
",j,j-1);
for (int x=1;x<=w;x++){
int now=c[x]+1;
for (int i=last[x];i;i=Next[i])
if (to[i]-w>0 && to[i]-w<=m){
int u=e[to[i]-w].x+e[to[i]-w].y-x;
for (int j=now;j<now+flow[i];j++)
printf("%d %d
",c[u],j);
now=now+flow[i];
}
}
}
void solve(){
for (int i=1;i<=m;i++)
f[e[i].x][e[i].y]-=b[i];
memset(last,0,sizeof(last));
edge=0;
for (int i=1;i<=w;i++)
Add(i,t,a[i]-1-f[i][i]);
for (int i=1;i<=m;i++)
if (f[e[i].x][e[i].y]){
Add(i+w,e[i].x,INF);
Add(i+w,e[i].y,INF);
Add(s,i+w,f[e[i].x][e[i].y]);
}
int ans=0;
while (bfs())
while (int di=Dfs(s,INF)) ans+=di;
if (ans==sum){
Printf();
exit(0);
}
for (int i=1;i<=m;i++)
f[e[i].x][e[i].y]+=b[i];
}
void dfs(int x,int y){//找生成树
if (y==w-1){
if (p()) solve();
return;
}
if (x>m) return;
b[x]=0,dfs(x+1,y);
if(f[e[x].x][e[x].y])b[x]=1,dfs(x+1,y+1);
}
int main(){
scanf("%d",&n);
w=cw(n);
for (int i=1;i<n;i++){
scanf("%s%s",&s1,&s2);
int w1=strlen(s1),w2=strlen(s2);
if (w1>w2) std::swap(w1,w2);
f[w1][w2]++;
}
int j=1;
for (int i=1;i<w;i++,j=j*10) a[i]=j*9,c[i]=j;
if (w==1) a[w]=n;
else a[w]=n-j+1;
c[w+1]=n+1;
c[w]=j;
sum=n-1-(w-1);
for (int i=1;i<=w;i++)
if (f[i][i]>=a[i]){
puts("-1");
return 0;
}else sum-=f[i][i];
m=0;
for (int i=1;i<=w;i++)
for (int j=i+1;j<=w;j++) e[++m]={i,j};
s=w+m+1,t=w+m+2;
dfs(1,0);
puts("-1");
return 0;
}