【题解】CF611H New Year and Forgotten Tree

【题解】CF611H New Year and Forgotten Tree

神题了...

题目描述

给定你一棵树,可是每个节点上的编号看不清了,只能辨别它的长度。现在用问号的个数代表每个节点编号那个数字的长度,请你还原这一颗树,任意输出一个方案,有PSJ\(SPJ\)来检验你的正确性。无解输出一行\(-1\)

输入格式:

第一行一个整数\(n\)
接下来 \(n-1\)行每行两个有问号构成的字符串,代表编号长度。

输出格式 :

若有解,直接输出\(n-1\)行,每行两个正整数,代表你还原的这颗树的边连接的两个节点。
若无解输出\(-1\)

solution_1:搜索套网络流

仔细思考一下,发现问号个数分别一样的边是本质相同的,我们把形如\((? ?\ , ??)\)这样的边先处理掉,直接连成一条链,然后把这条链看做一个点。

现在问题就变成了,给你五种不同颜色的点,相同颜色不连边,现在已知颜色两两相连的边的数目,请你构造一颗树出来。

怎么构造?可以每种颜色选定一个"关键点",先让关键点们生成一棵树,要求别的颜色连边过来必须连这一个点。我们抓出两种颜色来讨论,必须满足\(|a->b_{关键}|+|b->a_{关键}|=e[a][b]\),我们可以把所有的限制建模跑网络流,这样我们就知道从每种颜色连到另一种颜色的数目,就可以直接构造方案了。

但是有个问题,关键点之间生成的树对于答案是有影响的,所以还要先暴搜这(最多五个)点的连边情况,这个爆搜底数和指数都很小,可以当做常数。

然而我没写这种。

solution_2:Hall定理

hall定理:加入对于一个二分图存在完美匹配(已经把某一边匹配满了),设两边为\(X,Y,|X|\le|Y|\),那么在\(|X|\)中选出\(\forall k \in[0,|X|]\)个顶点,它向另一边连接的点数\(\ge k​\)

#include<bits/stdc++.h>
using namespace std;
inline int qr(){
      char c=getchar();
      register int ret=0,f=0;
      while(not isdigit(c)) f|=c==45,c=getchar();
      while(isdigit(c)) ret=ret*10+c-48,c=getchar();
      return f?-ret:ret;
}
inline int qrqr(){
      register char c=getchar();
      register int ret=0;
      while(c^'?')c=getchar();
      while(c=='?')++ret,c=getchar();
      return ret;
}
const int maxn=9;
int ten[maxn];
int e[maxn][maxn];
int cnt[maxn];
bool in[maxn];
#define pb(x,y) push_back(make_pair(x,y))
vector < pair< int , int > > ve;
int n,m;

inline bool chek(){
      for(register int t=0,edd=(1<<m)-1;t<edd;++t){
        int a(0),b(0);
        for(register int i=0;i<m;++i)
          if(t>>i&1) b+=cnt[i+1];
        for(register int f=0;f<m;++f)
          for(register int g=f;g<m;++g)
            if((t>>f&1)|(t>>g&1))
                  a+=e[f+1][g+1];
        if(a<b) return 0;
      }
      return 1;
}

int main(){
      freopen("Manchester.in","r",stdin);
      freopen("Manchester.out","w",stdout);
      int sav=qr();
      n=sav;
      ten[1]=1;
      for(register int t=2;t<=6;++t) ten[t]=ten[t-1]*10;
      while(sav) sav/=10,++m;
      for(register int t=1,t1,t2;t< n;++t){
        t1=qrqr();t2=qrqr();
        ++e[t1][t2];
        if(t1^t2)++e[t2][t1];
      }
      for(register int t=1;t<=5;++t) cnt[t]=ten[t+1]-ten[t];
      cnt[m]=n-ten[m]+1;
      if(not chek()) return puts("-1"),0;
      --cnt[1],in[1]=1,++ten[1];
      while(sav<n-1){
        for(register int t=1;t<=m;++t){
          if(in[t])
            for(register int i=1;i<=m;++i){
                  if(!e[i][t] || !cnt[i]) continue;
                  --e[i][t],--cnt[i];
                  if(t^i) --e[t][i];
                  if(chek()){
                    ve.pb(ten[t]-1,ten[i]);
                    ++ten[i];in[i]=1;++sav;
                  }
                  else {
                    ++e[i][t],++cnt[i];
                    if(t^i) ++e[t][i];
                  }
            }
        }
      }
      for(auto t:ve)
        printf("%d %d\n",t.first,t.second);
      return 0; 
}

猜你喜欢

转载自www.cnblogs.com/winlere/p/10638825.html