zoukankan      html  css  js  c++  java
  • [atAGC029F]Construction of a tree

    构造一张二分图,左边是$n$个点,右边是$n-1$个集合,按照点属于集合连边

    定义一组匹配的意义,即说明该点的父亲在该集合中选择

    利用dinic求出二分图的最大匹配,若不为$n-1$则无解,否则考虑如何去构造一组解:

    考虑左边剩下的未参与匹配的点$x$,将其作为根,并将所有含有$x$的集合所匹配的点都作为$x$的儿子,重复此过程(对于其他点,要加上一个“未被选择过”)

    事实上,这个过程可以看作从源点出发在残余网络上的一棵bfs树,合法当且仅当能bfs到所有点

    (能从$x$走到对应集合当且仅当其匹配的不是该集合,能从一个集合走到$y$当且仅当$y$匹配了该集合,同时$y$一定不会去选择其所匹配的集合中的点,因为一定被其父亲选择完毕)

    下面,我们来证明若某一种匹配方式不能做到,则其余都不行:

    由于根是任意的,换言之这个点作为根如果不行,其余点也都不行,因此确定了根

    考虑调整匹配方案,一定可以通过若干次对一个序列的循环(通过将调整建为一张图可以证明)

    若轮换前这一个集合中存在一个点能被搜到,按照轮换的顺序其余点都能被搜到,因此轮换前所有点都不能被搜到,即是一个内部的交换,没有意义

    总复杂度为$o(nsqrt{n})$,可以通过

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define N 100005
     4 #define oo 0x3f3f3f3f
     5 struct ji{
     6     int nex,to,len;
     7 }edge[N<<3];
     8 queue<int>q;
     9 pair<int,int>ans[N];
    10 int E,n,x,y,tot,head[N<<1],work[N<<1],d[N<<1];
    11 void add(int x,int y,int z){
    12     edge[E].nex=head[x];
    13     edge[E].to=y;
    14     edge[E].len=z;
    15     head[x]=E++;
    16     if (E&1)add(y,x,0);
    17 }
    18 bool bfs(){
    19     memset(d,oo,sizeof(d));
    20     d[0]=0;
    21     q.push(0);
    22     while (!q.empty()){
    23         int k=q.front();
    24         q.pop();
    25         for(int i=head[k];i!=-1;i=edge[i].nex)
    26             if ((edge[i].len)&&(d[edge[i].to]==oo)){
    27                 d[edge[i].to]=d[k]+1;
    28                 q.push(edge[i].to);
    29             }
    30     }
    31     return d[2*n]<oo;
    32 }
    33 int dfs(int k,int s){
    34     if (k==2*n)return s;
    35     for(int &i=work[k];i!=-1;i=edge[i].nex)
    36         if ((edge[i].len)&&(d[edge[i].to]==d[k]+1)){
    37             int p=dfs(edge[i].to,min(s,edge[i].len));
    38             if (p){
    39                 edge[i].len-=p;
    40                 edge[i^1].len+=p;
    41                 return p;
    42             }
    43         }
    44     return 0;
    45 }
    46 int dinic(){
    47     int k,ans=0;
    48     while (bfs()){
    49         memcpy(work,head,sizeof(work));
    50         while (k=dfs(0,oo))ans+=k;
    51     }
    52     return ans;
    53 }
    54 void bfs_build(){
    55     memset(d,0,sizeof(d));
    56     for(int i=head[0];i!=-1;i=edge[i].nex)
    57         if (edge[i].len){
    58             q.push(edge[i].to);
    59             d[edge[i].to]=edge[i].to;
    60         }
    61     while (!q.empty()){
    62         int k=q.front();
    63         q.pop();
    64         for(int i=head[k];i!=-1;i=edge[i].nex)
    65             if ((edge[i].to)&&(edge[i].len)&&(!d[edge[i].to])){
    66                 if (edge[i].to>n)d[edge[i].to]=d[k];
    67                 else{
    68                     tot++;
    69                     ans[k-n]=make_pair(d[k],edge[i].to);
    70                     d[edge[i].to]=edge[i].to;
    71                 }
    72                 q.push(edge[i].to);
    73             }
    74     }
    75 }
    76 int main(){
    77     scanf("%d",&n);
    78     memset(head,-1,sizeof(head));
    79     for(int i=1;i<=n;i++)add(0,i,1);
    80     for(int i=1;i<n;i++){
    81         scanf("%d",&x);
    82         add(i+n,2*n,1);
    83         for(int j=1;j<=x;j++){
    84             scanf("%d",&y);
    85             add(y,i+n,1);
    86         }
    87     }
    88     if (dinic()<n-1){
    89         printf("-1");
    90         return 0;
    91     }
    92     bfs_build();
    93     if (tot!=n-1)printf("-1");
    94     else{
    95         for(int i=1;i<n;i++)
    96             printf("%d %d
    ",ans[i].first,ans[i].second);
    97     }
    98 }
    View Code
  • 相关阅读:
    python实现测试中常用的脚本(待完善)
    python使用mysql数据库(虫师)
    jQuery中动画animate(上)
    jQuery事件对象的属性和方法
    扩展欧几里得算法详解
    jQuery事件对象的作用(利用冒泡事件优化)
    卸载事件off()方法
    on()的高级用法
    三种快速排序以及快速排序的优化
    on()的多事件绑定
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/14372658.html
Copyright © 2011-2022 走看看