【学习笔记】CF1935F Andrey‘s Tree

发个博客,证明我还活着。

答案很显然,根据 kruskal \text{kruskal} kruskal算法,尽量连 ( i , i + 1 ) (i,i+1) (i,i+1)是最优的,难点在于输出方案。下文设删掉的节点是 v v v

正常的做法是从小到大依次判断 ( i , i + 1 ) (i,i+1) (i,i+1)是否连通,如果不连通就把这条边加进去,这样可以保证前缀都是联通的,但是发现到 ( v − 1 , v + 1 ) (v-1,v+1) (v1,v+1)的时候就有问题了,因为这条边不能加进去,这个时候前缀就被分成了两段,直接做就很麻烦了。你可以试一试

这个时候我们可以考虑直接构造,也就是说不用记录这么多信息,并且我们不需要真的去判断这条边加入后是否合法,而是通过一些比较巧妙的方法去确保它合法,这通常需要一些特殊的手段。

考虑求出分开后的每个连通块内编号的最大值 m x mx mx和最小值 m i mi mi。首先我们发现,如果不存在一个连通块的 m i mi mi等于 v + 1 v+1 v+1,那么我们对每个连通块加入 ( m i , m i − 1 ) (mi,mi-1) (mi,mi1)这条边就是一个合法的构造。证明考虑把连通块按 m i mi mi从小到大排序,这样就得到了一个拓扑序(一定是从下标大的连向下标小的,可以看成一颗树)。

但是如果我们运气不好,恰好存在一个连通块的 m i mi mi等于 v + 1 v+1 v+1,不难发现这个时候只会剩下两个连通块,我们把两个连通块里面编号的最大值求出来,假设其中较小的一个是 m m m,如果 m > v m>v m>v那么连接 ( m , m + 1 ) (m,m+1) (m,m+1),否则连接 ( v − 1 , v + 1 ) (v-1,v+1) (v1,v+1)就做完了。这个做法的缺点是要算 i i i在哪颗子树中,要求的信息还是太多了,不够简洁。

其实,还存在更精细的构造,并且只用到了更少的信息和判断。我们考虑把 m i < v mi<v mi<v的连通块分入集合 L L L m i > v mi>v mi>v的连通块分入 R R R。首先我们判断如果 L L L m x mx mx的最大值 t < v t<v t<v,那么用上述构造再连接 ( v − 1 , v + 1 ) (v-1,v+1) (v1,v+1)即可。考虑对于 L L L中的连通块还是连接 ( m i , m i − 1 ) (mi,mi-1) (mi,mi1),但是对于 R R R中的连通块我们要反过来连接 ( m x , m x + 1 ) (mx,mx+1) (mx,mx+1),原因后面会讲。显然 L L L只会连向 L L L R R R可能连向 L L L R R R。考虑如果 t = n t=n t=n那么此时已经联通了,否则我们直接连 ( t , t + 1 (t,t+1 (t,t+1)就对了。因为我们前面已经保证了 t > v t>v t>v,并且因为 t t t L L L中最大的编号,所以 t + 1 t+1 t+1一定在 R R R中,我们发现我们发现这个时候 t + 1 t+1 t+1一定走不到 t t t(编号只会变大),发现这个时候恰好连了 d u v − 1 du_v-1 duv1条边,所以就对了。感觉最妙的还是想到把点分成两个部分,然后最后一步把最后这条边构造了出来。可以做到线性。

还有一种做法,需要每次加边时判断连通性,但是用到了均摊复杂度的思想。大致思路是分成子树内和子树外两个部分,发现子树内之间的连边只有当 L C A LCA LCA恰好是 v v v的时候才有用,但是 ( i , i + 1 ) (i,i+1) (i,i+1)只有一个 L C A LCA LCA的点,所以就对了。这个技巧也比较实用。

#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define db double
#define inf 0x3f3f3f3f
using namespace std;
const int N=2e5+5;
int T,n,rt,sz[N],mi[20][N],mx[20][N],dfn[N],num,fa[N];
vector<int>G[N];
void dfs(int u,int topf){
    dfn[u]=++num,mi[0][num]=mx[0][num]=u,sz[u]=1,fa[u]=topf;
    for(auto v:G[u]){
        if(v==topf)continue;
        dfs(v,u),sz[u]+=sz[v];
    }
}
int qmin(int l,int r){
    if(l>r)return inf;
    int k=__lg(r-l+1);
    return min(mi[k][l],mi[k][r-(1<<k)+1]);
}
int qmax(int l,int r){
    if(l>r)return -inf;
    int k=__lg(r-l+1);
    return max(mx[k][l],mx[k][r-(1<<k)+1]);
}
int main(){
    //freopen("data.in","r",stdin);
    // freopen("own.out","w",stdout);
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>T;
    while(T--){
        cin>>n;
        for(int i=1;i<=n;i++)G[i].clear();
        for(int i=1;i<n;i++){
            int x,y;
            cin>>x>>y;if(x>y)swap(x,y);
            G[x].pb(y),G[y].pb(x);
        }
        num=0,dfs(1,0);
        for(int i=1;i<=__lg(n);i++){
            for(int j=1;j<=n-(1<<i)+1;j++){
                mi[i][j]=min(mi[i-1][j],mi[i-1][j+(1<<i-1)]);
                mx[i][j]=max(mx[i-1][j],mx[i-1][j+(1<<i-1)]);
            }
        }
        for(int i=1;i<=n;i++){
            vector<pair<int,int>>res;
            vector<pair<int,int>>L,R;
            int ok=0;
            for(auto j:G[i]){
                if(j!=fa[i]){
                    pair<int,int>x={qmin(dfn[j],dfn[j]+sz[j]-1),qmax(dfn[j],dfn[j]+sz[j]-1)};
                    if(x.fi<i)L.pb(x);
                    else R.pb(x);
                }
            }
            if(i>1){
                pair<int,int>x={min(qmin(1,dfn[i]-1),qmin(dfn[i]+sz[i],n)),max(qmax(1,dfn[i]-1),qmax(dfn[i]+sz[i],n))};
                if(x.fi<i)L.pb(x);
                else R.pb(x);
            }
            if(i==1){
                for(auto e:R){
                    if(e.fi!=2)res.pb({e.fi,e.fi-1});
                }
            }
            else if(i==n){
                for(auto e:L){
                    if(e.fi!=1)res.pb({e.fi,e.fi-1});
                }
            }
            else{
                int tmp=0;
                for(auto e:L)tmp=max(tmp,e.se);
                if(tmp<i){
                    for(auto e:L){
                        if(e.fi!=1)res.pb({e.fi,e.fi-1});
                    }
                    for(auto e:R){
                        if(e.fi!=i+1)res.pb({e.fi,e.fi-1});
                    }
                    res.pb({i-1,i+1});
                    ok=1;
                }
                else{
                    for(auto e:L){
                        if(e.fi!=1)res.pb({e.fi,e.fi-1});
                    }
                    for(auto e:R){
                        if(e.se!=n)res.pb({e.se,e.se+1});
                    }
                    if(tmp!=n){
                        res.pb({tmp,tmp+1});
                    }
                }
            }
            cout<<res.size()+ok<<" "<<res.size()<<"\n";
            for(auto e:res)cout<<e.fi<<" "<<e.se<<"\n";
            cout<<"\n";
        }
    }
}

相关推荐

  1. 学习笔记CF1835C Twin Clusters

    2024-04-05 21:52:06       57 阅读
  2. 学习笔记CF1935F Andrey‘s Tree

    2024-04-05 21:52:06       34 阅读
  3. CF 1901B Chip and Ribbon 学习笔记

    2024-04-05 21:52:06       61 阅读
  4. 题解:CF1934A(Too Min Too Max)

    2024-04-05 21:52:06       42 阅读
  5. 学习笔记CF1349F2 Slime and Sequences (Hard Version)

    2024-04-05 21:52:06       59 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-05 21:52:06       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-05 21:52:06       106 阅读
  3. 在Django里面运行非项目文件

    2024-04-05 21:52:06       87 阅读
  4. Python语言-面向对象

    2024-04-05 21:52:06       96 阅读

热门阅读

  1. 栈的应用:20. 有效的括号LeetCode

    2024-04-05 21:52:06       35 阅读
  2. 【六 (1)机器学习-机器学习算法简介】

    2024-04-05 21:52:06       33 阅读
  3. C语言每日一题—日期转换问题

    2024-04-05 21:52:06       47 阅读
  4. B000-1114-常量 变量 数据类型

    2024-04-05 21:52:06       38 阅读