[HNOI2008]神奇的国度

Description

BZOJ1006

Solution

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

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

求完美序列可以用最大势能法(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;
}

Note

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

posted @ 2018-07-29 16:49  wyxwyx  阅读(164)  评论(0编辑  收藏  举报