【笔记】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;
}
标签:ch,rp,mat,int,定理,笔记,fa,Dilworth,op 来源: https://www.cnblogs.com/7KByte/p/16278374.html