[BZOJ1006][HNOI2008]神奇的国度

这个题就是经典的最小点染色问题的模型。

对于弦图(题目中保证只有三角形,一定是弦图),可以按其完美序列(一个点的排列,使得每个点都是后缀的单纯点(邻居节点构成完全图的点))逆序依次染色,即可求得最小染色。

求完美序列可以用最大势能法(mcs),算法的过程为每次找出一个势能最大的点,放到当前序列的开头,并将不在序列中的该节点的邻居节点的势能增加,即可求出完美序列。正确性没有找到证明。使用链表(桶)可以做到\(O(n+m)\)的复杂度。

由于求完美序列的顺序和染色顺序一致(逆序),所以可以同时进行这两个步骤。

code:

#include <cstdio>
#include <cstring>
#include <queue>

const int N = 10000 + 10;
const int M = 2000000 + 10;

int hd[N], to[M], nxt[M], cnt;
int col[N], flag[N], label[N], vis[N];
int lhd[N], lst[M], tot, w[M];
int ans, n, m;

inline void adde(int x, int y) {
    cnt++;
    to[cnt] = y;
    nxt[cnt] = hd[x];
    hd[x] = cnt;
}

inline void push(int x) {
    tot++;
    w[tot] = x;
    lst[tot] = lhd[label[x]];
    lhd[label[x]] = tot;
}

inline void mcs() {
    int mxc = 0;
    for (int i = 1; i <= n; ++i) push(i);

    for (int i = n; i; --i) {
        int cur = lhd[mxc];
        while (flag[w[cur]]) {
            lhd[mxc] = cur = lst[cur];
            while (!cur) {
                mxc--;
                cur = lhd[mxc];
            }
        }
        
        flag[w[cur]] = 1;
        memset(vis, 0, sizeof vis);
        for (int j = hd[w[cur]]; j; j = nxt[j]) {
            if (!flag[to[j]]) {
                if (++label[to[j]] > mxc) mxc = label[to[j]];
                push(to[j]);
            } else {
                vis[col[to[j]]] = 1;
            }
        }
        for (int j = 1; j <= n; ++j) if (!vis[j]) {
            col[w[cur]] = j;
            if (j > ans) ans = j;
            break;
        }
    }
    printf("%d\n", ans);
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1, x, y; i <= m; ++i) {
        scanf("%d%d", &x, &y);
        adde(x, y);
        adde(y, x);
    }
    mcs();
    return 0;
}

后记:开始刷八中的这两天,突然觉得自己会的算法太少了,有些算法即使会也难以应用,还是要努力学习,多刷题,理解更多的算法和模型,争取早日摆脱蒟蒻的行列。

猜你喜欢

转载自www.cnblogs.com/wyxwyx/p/bzoj1006.html