<題解>[IOI2019]景點劃分
這個題對我來說可以算是超出了我的能力範圍
被學長拿來教我做構造,構造題真簡單,構造題真是人,構造題真能手切。。。
首先對於本題,必須要知道dfs樹這東西,就是在一個圖中得到一個樹,
簡單來說就是吧所有的邊分成樹邊和非樹邊,所有的n-1個樹邊會把所有點連線成一顆樹
這個在實現上就是一個dfs就好了
這裡有一個小小的性質,在一個dfs樹中,所有的非樹邊一定不是橫叉邊
意思就是所有的邊連線的兩個點一定是祖先後代關係
那麼這個題就可以做了
你會發現這a,b,c並沒有特殊的要求,所以我們選擇這其中較小的那兩個一定是優的
所以我們直接扔掉最大的那個,這時候你會發現
為了保證我們很順利的找到答案,我們必須要找到樹的重心,
因為在重心兩側,子樹的大小一定小於等於\(\frac{n}{2}\),而a,b的大小又小於等於\(\frac{n}{3}\)
這樣的話,我們只要儲存這個重心,我們可以任意連線子樹,從而一定可以滿足\(a,b\)的其中一個
而剩下的子樹中的節點數又一定大於a,這樣為我們找到答案提供了很大的方便
那麼我們一定先滿足\(b\),因為我們要是想湊出a來一定是比b更容易,那麼我們接下來的重點就是要找a
首先我們看\(m=n-1\)的情況,這樣的話本來就是一棵樹,直接找到重心,判斷是不是有子樹的大小大於a,如果有 ,那麼就有解,沒有的話,就無解唄
因為你沒有別的邊可以連線另外的子樹,所以你當前的子樹就你你可以找到的所有可以作為a的聯通塊
而b可以通過重心連線,不需要考慮
那麼這時候你會發現,如果是圖的話,那就可以有別的邊來連線??雖然不可以連線在dfs樹上的子樹,
但是別忘了,在dfs樹上,它的祖先也是他的子樹,所以我們只要不斷的去連線和它祖先有連邊的子樹,並且將大小加和
如果能夠得到一個大小為a的聯通塊,那就有解,當然這時候你並沒有選重心,b仍然是合法的
我們來看這裡的重心有什麼用,這個重心首先保證了你有一堆大小\(<=\frac{n}{2}\)的子樹
這樣的話,你一定可以湊出一個b來,而重心又在一定程度上幫你將整張圖分為兩部分
一個是那一堆子樹,一個是祖先那邊的節點,這就是構造的意義所在
構造你想要的條件來解決一般問題
還有一些要注意的情況,
有可能在圖中,你並不能找到合法的反祖邊,但是呢,有一顆子樹的大小是合法的,
這個時候就要向樹的做法一樣了,我要去判斷一下每一顆子樹的大小
還有輸出的時候,千萬不要找到一顆子樹就開始從他的根節點遍歷
因為你是從有和祖先連邊的那個點出發才可以聯通的啊,要從他開始
我的程式碼中imp這個陣列就是幹這個活的
AC_code
#include <bits/stdc++.h>
using namespace std;
#define re register int
const int N = 1e5 + 5;
const int M = 2e5 + 5;
int n, m;
pair<int, int> a[4];
int fr[M * 2], to[M * 2], nxt[M * 2], head[N], rp = 1;
void add_edg(int x, int y) {
to[++rp] = y;
fr[rp] = x;
nxt[rp] = head[x];
head[x] = rp;
}
int fa[N], dep[N], siz[N], rt, mn = 0x3f3f3f3f;
bool vis[N], pd[M * 2];
void dfs(int x) {
vis[x] = true;
siz[x] = 1;
int mx = 0;
for (re i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (vis[y])
continue;
fa[y] = x;
pd[i] = pd[i ^ 1] = true;
dep[y] = dep[x] + 1;
dfs(y);
siz[x] += siz[y];
if (siz[y] > mx)
mx = siz[y];
}
if (n - siz[x] > mx)
mx = n - siz[x];
if (mx < mn)
mn = mx, rt = x;
}
int imp[N], bl[N], sz[N];
void change(int x, int b) {
bl[x] = b;
sz[b]++;
for (re i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (dep[y] != dep[x] + 1)
continue;
change(y, b);
}
}
int ji[N], cnt;
int ans[N];
void biao1(int x) {
if (cnt == a[1].first)
return ;
ans[x] = a[1].second;
cnt++;
for (re i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (y == rt || dep[y] != dep[x] + 1)
continue;
biao1(y);
}
}
void biao2(int x, int f) {
if (cnt == a[1].first || ans[x])
return ;
ans[x] = a[1].second;
cnt++;
for (re i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (bl[y] != f || ans[y])
continue;
biao2(y, f);
}
}
void biao3(int x) {
if (cnt == a[2].first)
return ;
ans[x] = a[2].second;
cnt++;
for (re i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (dep[y] != dep[x] + 1)
continue;
biao3(y);
}
}
signed main() {
scanf("%d%d", &n, &m);
scanf("%d%d%d", &a[1].first, &a[2].first, &a[3].first);
a[1].second = 1;
a[2].second = 2;
a[3].second = 3;
sort(a + 1, a + 4);
for (re i = 1, x, y; i <= m; i++) {
scanf("%d%d", &x, &y);
x++;
y++;
add_edg(x, y);
add_edg(y, x);
}
dfs(1);
siz[0] = n - 1;
//cout<<rt<<endl;
for (re i = head[rt]; i; i = nxt[i]) {
int y = to[i];
if (y == fa[rt] || dep[y] != dep[rt] + 1)
continue;
change(y, y);
//cout<<rt<<" "<<y<<" "<<siz[0]<<" "<<siz[y]<<endl;
siz[0] -= sz[y];
}
//cout<<rt<<" "<<siz[rt]<<" "<<siz[0]<<endl;
for (re i = 2; i <= rp; i += 2) {
if (pd[i])
continue;
//cout<<"sb"<<endl;
if (siz[0] >= a[1].first)
break;
int u = fr[i], v = to[i];
//cout<<bl[u]<<" "<<bl[v]<<endl;
if (bl[u] && bl[v])
continue;
if (!bl[u] && !bl[v])
continue;
//cout<<u<<" "<<v<<" "<<siz[bl[u]]<<" "<<siz[bl[v]]<<endl;
if (bl[u] && !ji[bl[u]] && (v != rt || siz[0] == 0)) {
ji[bl[u]] = 1;
imp[bl[u]] = u;
siz[0] += sz[bl[u]];
}
if (bl[v] && !ji[bl[v]] && (u != rt || siz[0] == 0)) {
ji[bl[v]] = 1;
imp[bl[v]] = v;
siz[0] += sz[bl[v]];
}
}
//cout<<siz[0]<<endl;
int flag = 0;
if (siz[0] < a[1].first) {
for (re i = head[rt]; i; i = nxt[i]) {
int y = to[i];
if (dep[y] != dep[rt] + 1)
continue;
if (siz[y] >= a[1].first) {
siz[0] = siz[y];
ji[y] = 1;
imp[y] = y;
flag = 1;
break;
}
}
}
if (siz[0] >= a[1].first) {
cnt = 0;
if ((rt != 1 && !flag))
biao1(1);
for (re i = head[rt]; i; i = nxt[i]) {
int y = to[i];
if (!ji[y] || dep[y] != dep[rt] + 1)
continue;
if (cnt >= a[1].first)
break;
biao2(imp[y], y); //cout<<siz[y]<<endl;
}
cnt = 1;
ans[rt] = a[2].second;
for (re i = head[rt]; i; i = nxt[i]) {
int y = to[i];
if (ji[y] || dep[y] != dep[rt] + 1)
continue;
if (cnt >= a[2].first)
break;
biao3(y);
}
}
//cout<<a[1].second<<endl;
for (re i = 1; i <= n; i++) {
if (siz[0] >= a[1].first && !ans[i])
ans[i] = a[3].second;
printf("%d ", ans[i]);
}
//cout<<p<<endl;
}