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\n",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\n",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\n",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;
}

  

posted @ 2021-05-16 23:32  蒟蒻JHY  阅读(88)  评论(0编辑  收藏  举报