Loading

【笔记】Dilworth定理

给定一个有向无环图,我们称 \(x,y\) 存在关系当且仅当存在 \(x\to y\) 或者 \(y \to x\) 的边。最长链为最大的集合使得其中任意两个元素存在关系,最长反链为最大的集合使得其中任意两个元素不存在关系。

Dilworth 定理:最长反链等于最小链覆盖。

最小链覆盖为用最少的链(可以相交)覆盖图的每一个点,我们传递闭包后即可转化为经典二分图模型:最小链划分。

如何求出具体方案?我们先求出链划分,然后先选取每条链的终点,只要存在两个点之间有关系,如 \(x\to y\),就将 \(y\) 沿着对应的链向起点走。可以证明重复这个过程可以得到答案。

听起来很复杂,但实现很简单。下面是 CF590E 的代码。

#define N 10000005
#define M 755
char s[N + M]; int idx, n, ch[N][2], fa[N], w[N], u[M], d[M][M], m, v[M], mat[M];
queue<int>q;
bool dfs(int x){
	rp(i, n)if(d[x][i] && !v[i]){
		v[i] = 1;
		if(!mat[i] || dfs(mat[i])){mat[i] = x; return true;}
	}return false;
}
vector<int>c;
int main() {
	read(n);
	rp(i, n){
		u[i] = ++m; scanf("%s", s + m);
		int l = strlen(s + m), x = 0;
		rp(j, l){
			int op = s[j + m - 1] - 'a';
			if(!ch[x][op])ch[x][op] = ++idx;
			x = ch[x][op];
		}
		w[x] = i, m += l;
	}
	if(ch[0][0])q.push(ch[0][0]);
	if(ch[0][1])q.push(ch[0][1]);
	while(!q.empty()){
		int x = q.front(); q.pop();
		if(!w[x])w[x] = w[fa[x]];
		if(ch[x][0])fa[ch[x][0]] = ch[fa[x]][0], q.push(ch[x][0]);
		else ch[x][0] = ch[fa[x]][0];
		if(ch[x][1])fa[ch[x][1]] = ch[fa[x]][1], q.push(ch[x][1]);
		else ch[x][1] = ch[fa[x]][1];
	}
	rp(i, n){
		int k = strlen(s + u[i]), x = 0;
		rp(j, k){
			if(w[x])d[i][w[x]] = 1;
			x = ch[x][s[j + u[i] - 1] - 'a'];
		}
		if(w[fa[x]])d[i][w[fa[x]]] = 1;
	}
	rp(k, n)rp(i, n)rp(j, n)d[i][j] |= d[i][k] & d[k][j];
	int ans = n;
	rp(i, n){
		memset(v, 0, sizeof(v));
		if(dfs(i))ans--;
	}
	printf("%d\n", ans);
	memset(v, 0, sizeof(v));
	rp(i, n)v[mat[i]] = 1;
	rp(i, n)if(!v[i])c.pb(i);
	while(true){
		memset(v, 0, sizeof(v));
		go(x, c)rp(i, n)if(d[x][i])v[i] = 1;
		int p = 0;
		for(auto &x : c)while(v[x])x = mat[x], p = 1;
		if(!p)break;
	}
	go(x, c)printf("%d ", x);el;
	return 0;
}
posted @ 2022-05-16 20:18  7KByte  阅读(209)  评论(0编辑  收藏  举报