CF611H New Year and Forgotten Tree

题意

给定你一棵树,结点编号为 \(1-n\) 现在用问号的个数代表每个节点编号那个数字的长度,请你还原这一颗树,任意输出一个方案,可能无解。
传送门
\(n<2 \times 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\n",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\n",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\n",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;
}

猜你喜欢

转载自www.cnblogs.com/flyfeather6/p/12213027.html