2017-2018 ACM-ICPC, NEERC, Northern Subregional Contest (Gym - 101612H)H - Hidden Supervisors(贪心)

题目链接:http://codeforces.com/gym/101612/attachments

题目大意:给出一个有n个结点,且为若干个联通块组成的图,同时保证每个联通块都是一棵有根树。现在要你将这若干个联通块连边(只能由根节点向别的联通块连边),使得只剩下一个联通块,同时这个联通块也是有根树,而且要使得树内的相连二元组尽可能的多(树上的点每个点只能属于一个相连二元组,同时每个相连二元组内的点都是有边相连的)。要你输出最多的相连二元组的数量,和最终联通后每个结点的父亲结点(联通后的树以节点 1 为根节点)。

题目思路:对于一开始不相连的各个有根树,我们只需要从这个有根树的叶子结点开始向上贪心做预处理,就能求出每个联通块内最多有多少个相连二元组,以及这个联通块内哪些结点是不会组成相连二元组,同时也可以知道这个联通块的根节点是否会与其它结点组成相连二元组。

接下来可以按联通块的根节点是否被选为某个二元组来对这些联通块进行分类。

对于那些根节点已经被选为某个二元组的联通块,我们只需要将根节点向节点 1 连边就可以了,因为这种联通块的根节点无论跟哪个点相连,都不会使得总的二元组的数量增加,所以我们直接令它和节点 1 连边就可以了。在将这些联通块连接时,我们同时还要记录哪些结点是未被选为二元组的,以便后面的计算。

对于那些根节点并未被选为某个二元组的联通块,我们按联通块内未被选为二元组的结点的个数从大到小进行排序。接下来再按顺序将根节点向已经连成的最大的联通块内未被选为二元组的结点连边即可,连完后再对未被选为二元组的结点进行更新,不断更新维护即可。

最后再说说为啥按联通块内未被选为二元组的结点的个数从大到小进行排序之后再进行连边的方案是最优的,由于每个未被选为二元组的根节点只要和前面的联通块内未被选为二元组的结点连边就可以使得最终的答案增加,那么我们为了使得答案尽可能的大,就要使得前面的联通块内未被选为二元组的结点尽可能的多,这样自然是从大到小排序就会是最优的了。

具体实现看代码:

#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pb push_back
#define MP make_pair
#define lowbit(x) x&-x
#define clr(a) memset(a,0,sizeof(a))
#define _INF(a) memset(a,0x3f,sizeof(a))
#define FIN freopen("in.txt","r",stdin)
#define IOS ios::sync_with_stdio(false)
#define fuck(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int>pii;
const int MX = 1e5 + 5;
const int inf = 0x3f3f3f3f;

int n;
int ans;
bool match[MX];
vector<int>G[MX], root, no_match, ver;
int fa[MX];
struct Block {
    int sz, rt;
    vector<int>vec;
    Block() {}
    Block(int sz, int rt, vector<int>vec): sz(sz), rt(rt), vec(vec) {}

    bool operator<(const Block &A)const {
        return sz > A.sz;
    }
} a[MX];

void dfs(int u) {
    for (auto v : G[u]) {
        dfs(v);
        if (!match[u] && !match[v]) {
            match[u] = 1; match[v] = 1;
            ans++;
        }
        if (!match[v]) ver.pb(v);
    }
}

int main() {
    freopen("hidden.in", "r", stdin);
    freopen("hidden.out", "w", stdout);
    scanf("%d", &n);
    root.pb(1);
    for (int i = 2, p; i <= n; i++) {
        scanf("%d", &p);
        if (!p) root.pb(i);
        else G[p].pb(i), fa[i] = p;
    }
    int cnt = 0;
    for (auto rt : root) {
        ver.clear();
        dfs(rt);
        if (rt == 1 || match[rt]) {
            fa[rt] = 1;
            for (auto v : ver) no_match.pb(v);
            if (rt == 1 && !match[1]) no_match.pb(1);
        } else
            a[cnt++] = Block(ver.size(), rt, ver);
    }
    sort(a, a + cnt);
    for (int i = 0; i < cnt; i++) {
        int rt = a[i].rt;
        if (no_match.size()) {
            fa[rt] = no_match.back();
            no_match.pop_back(); ans++;
        } else {
            fa[rt] = 1;
            no_match.pb(rt);
        }
        for (auto now : a[i].vec) {
            no_match.pb(now);
        }
    }
    printf("%d\n", ans);
    for (int i = 2; i <= n; i++)
        printf("%d%c", fa[i], " \n"[i == n]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Lee_w_j__/article/details/83902958