zoukankan      html  css  js  c++  java
  • Codeforces Round #720 (Div. 2) D. Nastia Plays with a Tree

    题面

    最近偶尔回归一下,记录做过的一些题(doge

    这题其实就是问最多留下原树中的多少条边,使得没有一个点的度数>2(因为再把所有零散的链连起来就是一整条链了)。这就是一个简单的树上dp,直接做就行。

    但坑的是它还要求方案。

    好么,我们dp的时候记录一下每个点最优方案中的边都是连向哪的,然后再dfs一遍就可以得到删边之后森林的形状了(说起来容易,写起来吐血)。

    最后的最后,我们还需要把每条删的边找到一条对应的新边,这时我们就要在线记录每个联通分量对应的链的两个端点,并且要支持两个联通分量的合并。好么,这不就是并查集吗。

    然后我们就做完了哈哈哈!

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int N=100005;
    #define pb push_back
     
    int f[N][3],n,t,to[N][3],min_pos[N],p[N],ccnt,Side[N][2],color[N],get_fa[N];
    vector<int> g[N];
    bool ban[N];
     
    inline void init(int x){
        for(int i=1;i<=x;i++) g[i].clear(),ban[i] = to[i][0] = to[i][1] = to[i][2] = 0;
        for(;ccnt;ccnt--) Side[ccnt][0] = Side[ccnt][1] = 0;
    }
     
    int get_pa(int x){ return get_fa[x] == x?x:(get_fa[x] = get_pa(get_fa[x]));}
     
    void dfs(int x,int fa){
        p[x] = fa;
     
        int base = 0,MIN = 1e8,MIN_ADD_SECMIN = 1e9,son = 0;
        int son_min,son_amin,derta,son_amin_from;
     
        for(int s:g[x]) if(s!=fa){
            dfs(s,x),son++;
     
            son_amin = min(f[s][0],f[s][1]);
            son_amin_from = f[s][0]<f[s][1] ? 0 : 1;
            son_min = min(son_amin,f[s][2]);
            derta = son_amin - son_min;
     
            base += son_min;
            if(MIN + derta < MIN_ADD_SECMIN){
                MIN_ADD_SECMIN = MIN + derta;
                if(derta <= MIN) to[x][2] = to[x][0],to[x][0] = (n+1) * son_amin_from + s;
                else to[x][2] = (n+1) * son_amin_from + s;
            }
            if(derta < MIN) MIN = derta,to[x][1] = (n+1) * son_amin_from + s;
        }
     
        f[x][0] = base + son;
        f[x][1] = base + MIN + son - 1;
        f[x][2] = base + MIN_ADD_SECMIN + son - 2;
     
        min_pos[x] = 0;
        if(f[x][1] < f[x][min_pos[x]]) min_pos[x] = 1;
        if(f[x][2] < f[x][min_pos[x]]) min_pos[x] = 2;   
    }
     
    void dfss(int x,int type,const int &ha){
        //printf("%d %d
    ",x,type);
        if(type < 0) type = min_pos[x],ban[x] = 1;
        for(int s:g[x]) if(s!=p[x]){
            if(type == 1 && to[x][1] % ha == s) dfss(s,to[x][1] / ha,ha);
            else if(type == 2){
                if(to[x][0] % ha == s) dfss(s,to[x][0] / ha,ha);
                else if(to[x][2] % ha == s) dfss(s,to[x][2] / ha,ha);
                else dfss(s,-1,ha);
            }
            else dfss(s,-1,ha);
        }
    }
     
    void dfsss(int x){
        color[x] = ccnt;
        int newson = 0;
        for(int s:g[x]) if(s!=p[x] && !ban[s])
            dfsss(s),newson++;
        if(newson == 0)
            if(!Side[ccnt][1]) Side[ccnt][1] = x;
            else Side[ccnt][0] = Side[ccnt][1],Side[ccnt][1] = x;
    }
     
    inline void output(){
        printf("%d
    ",min(f[1][0],min(f[1][1],f[1][2])));
        for(int i=2,fa,fb;i<=n;i++) if(ban[i]){
            fa = get_pa(color[i]),fb = get_pa(color[p[i]]);
            printf("%d %d %d %d
    ",i,p[i],Side[fa][0],Side[fb][1]);
            get_fa[fb] = fa,Side[fa][0] = Side[fb][0];
        }
    }
     
    int main(){
        for(scanf("%d",&t);t--;){
            init(n),scanf("%d",&n);
            for(int u,v,i=1;i<n;i++)
                scanf("%d%d",&u,&v),g[u].pb(v),g[v].pb(u);
     
            dfs(1,0);
            const int ha = n+1;
            dfss(1,-1,ha);
            for(int i=1;i<=n;i++) if(ban[i]) ccnt++,Side[ccnt][0] = i,dfsss(i);
            for(int i=1;i<=ccnt;i++) get_fa[i] = i,Side[i][1] = Side[i][1] == 0 ? Side[i][0]:Side[i][1];
            output();
        }
     
        return 0;
    }
    

      

    我爱学习,学习使我快乐
  • 相关阅读:
    MsSql数据库存储过程加密解密
    Delete Exists
    SQL Server 2008数据库日志收缩
    MSSQL PIVOT 实现行列转置
    Oracle中将查询出的多条记录的某个字段拼接成一个字符串的方法
    oracle的分析函数over 及开窗函数
    Asprise-OCR的使用
    【整理】动态加载Web Services
    【转载】Python正则表达式指南
    Mac中PyCharm设置GitHub的步骤
  • 原文地址:https://www.cnblogs.com/JYYHH/p/14775345.html
Copyright © 2011-2022 走看看