1. 程式人生 > 其它 ><題解>[IOI2019]景點劃分

<題解>[IOI2019]景點劃分

題目傳送門(luogu)
題目傳送門(loj)

這個題對我來說可以算是超出了我的能力範圍

被學長拿來教我做構造,構造題真簡單,構造題真是人,構造題真能手切。。。

首先對於本題,必須要知道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;
}