cf 911F 树的直径+贪心

$des$ 

给定一棵 n 个节点的树,你可以进行 n ? 1 次操作,每次操作步骤如下:
选择 u,v 两个度数为 1 的节点。
将 u,v 之间的距离加到 ans 上。
将 u 从树上删除。
求一个操作序列使得 ans 最大。

$sol$

先把直径拿出来,将直径外的点一个一个的和直径中的某一个端点配对并删掉。最
后再将直径删掉。这样就是对的。
如果当前直径外已经没有点了,那么显然只能将直径的端点删掉。否则一定不会去
删直径的端点。
因为先删一个直径外的点再删直径端点一定是不劣于先删直径端点再删这个直径外
的点的。

$code$

#include <bits/stdc++.h>

using namespace std;

#define gc getchar()
inline int read() {
    int x = 0; char c = gc;
    while(c < '0' || c > '9') c = gc;
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = gc;
    return x;

}

#define LL long long
#define Rep(i, a, b) for(int i = a; i <= b; i ++)
#define E exit(0)

const int N = 2e5 + 10;

int disa[N], disb[N], fa[N], A[N], topp[N], size[N], son[N], deep[N], sonjs[N];
bool Be_calc[N], vis[N], is_chain[N];
int Cut[N], js;

struct Node {int v, nxt;} G[N << 1];
int head[N], cnt;

int n, One, Tow;

void Link(int u, int v) {
    G[++ cnt].v = v; G[cnt].nxt = head[u]; head[u] = cnt;
}

int Bfs(int start, int dis[]) {
    queue <int> Q;
    memset(vis, 0, sizeof vis);
    dis[start] = 0;
    vis[start] = 1;
    Q.push(start);
    while(!Q.empty()) {
        int topp = Q.front();
        Q.pop();
        for(int i = head[topp]; ~ i; i = G[i].nxt) {
            int v = G[i].v;
            if(!vis[v]) {
                dis[v] = dis[topp] + 1;
                vis[v] = 1;
                Q.push(v);
            }
        }
    }
    int ret, retdis = -1;
    Rep(i, 1, n) if(dis[i] > retdis) ret = i, retdis = dis[i];
    return ret;
}

void Dfs1(int u, int f_, int dep) {
    fa[u] = f_, deep[u] = dep; size[u] = 1;
    for(int i = head[u]; ~ i; i = G[i].nxt) {
        int v = G[i].v;
        if(v == f_) continue;
        sonjs[u] ++;
        Dfs1(v, u, dep + 1);
        size[u] += size[v];
        if(size[v] > size[son[u]]) son[u] = v;
    }
}

void Dfs2(int u, int tp) {
    topp[u] = tp;
    if(!son[u]) {
        Cut[++ js] = u;
        return ;
    }
    Dfs2(son[u], tp);
    for(int i = head[u]; ~ i; i = G[i].nxt) {
        int v = G[i].v;
        if(v != fa[u] && v != son[u]) Dfs2(v, v);
    }
}

inline int Lca(int x, int y) {
    int tpx = topp[x], tpy = topp[y];
    while(tpx != tpy) {
        if(deep[tpx] < deep[tpy]) swap(x, y), swap(tpx, tpy);
        x = fa[tpx], tpx = topp[x];
    }
    if(deep[x] < deep[y]) swap(x, y);
    return y;
}

void Find_chain() {
    int lca = Lca(One,Tow);
    int tmp1 = One, tmp2 = Tow;
    while(tmp1 != lca) {
        is_chain[tmp1] = 1;
        tmp1 = fa[tmp1];
    }
    while(tmp2 != lca) {
        is_chain[tmp2] = 1;
        tmp2 = fa[tmp2];
    }
    is_chain[lca] = 1;
}

int o_1[N], o_2[N], o_3[N], tot;

int main() {
    n = read();
    Rep(i, 1, n) head[i] = -1;
    Rep(i, 1, n - 1) {
        int u = read(), v = read(); Link(u, v), Link(v, u);
    }
    
    One = Bfs(1, disa);
    Tow = Bfs(One, disa);
    Bfs(Tow, disb);
    Dfs1(One, 0, 1);
    Dfs2(One, One);
    Find_chain();

    LL Answer = 0;
    Rep(i, 1, js) {
        if(is_chain[Cut[i]] || Be_calc[Cut[i]]) continue;
        int now = Cut[i];
        
        while(!Be_calc[now] && !is_chain[now] && !sonjs[now]) {
            if(disa[now] > disb[now]) {
                o_1[++ tot] = One, o_2[tot] = now, o_3[tot] = now;
                Answer += disa[now];
            } else {
                o_1[++ tot] = Tow, o_2[tot] = now, o_3[tot] = now;
                Answer += disb[now];
            }
            Be_calc[now] = 1;
            now = fa[now];
            sonjs[now] --;
        }
    }
    
    int lca = Lca(One, Tow);
    
    while(One != lca) {
        o_1[++ tot] = One, o_2[tot] = Tow, o_3[tot] = One;
        Answer += disb[One];
        One = fa[One];
    }
    while(Tow != lca) {
        o_1[++ tot] = Tow, o_2[tot] = lca, o_3[tot] = Tow;
        Answer += (deep[Tow] - deep[lca]);
        Tow = fa[Tow];
    }
    
    cout << Answer << "\n";
    Rep(i, 1, tot) {
        cout << o_1[i] << " " << o_2[i] << " " << o_3[i] << "\n";
    }
    return 0;
}

 

posted @ 2018-10-11 10:13  xayata  阅读(187)  评论(0编辑  收藏  举报