zoukankan      html  css  js  c++  java
  • 【题解】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
    ",t.first,t.second);
          return 0; 
    }
    
    
  • 相关阅读:
    2019牛客暑期多校训练营(第八场)A All-one Matrices(单调栈+前缀和)
    2019牛客暑期多校训练营(第三场)A Graph Games(分块)
    2019牛客暑期多校训练营(第二场)E MAZE(线段树维护矩阵+DP)
    2019牛客暑期多校训练营(第二场)D Kth Minimum Clique(bitset+暴力枚举)
    2019牛客暑期多校训练营(第一场)H XOR(线性基)
    2019牛客暑期多校训练营(第六场)D Move(multiset+枚举)
    2019牛客暑期多校训练营(第五场)H subsequence 2(拓扑排序)
    2019牛客暑期多校训练营(第六场)J Upgrading Technology(矩阵前缀和+最小子串和+贪心)
    2019牛客暑期多校训练营(第五场)G subsequence 1(dp+组合数)
    P3857 [TJOI2008]彩灯(线性基)
  • 原文地址:https://www.cnblogs.com/winlere/p/10638825.html
Copyright © 2011-2022 走看看