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; }