luogu4298 [CTSC2008]祭祀

题目链接

problem

给出一个有向无环图,找出最多的点,使他们两两之间不能到达。
输出:
最多找到的点数,
输出一种方案,
输出每个点是否存在一种方案使某个点可以被选出。

Dilworth定理

偏序集

设R是集合A上的一个关系,R满足自反性,反对称性,传递性。那么就称R是集合A的偏序关系。

集合A和关系R共同组成偏序集。

例如,对于一个整数集合A,定义关系R为\(\le\)。那么A和R就组成了一个偏序集,这个偏序集中任意两个元素都是可比的,因为任意两个整数都可以比较大小。但并不是所有偏序集中的任意两个元素都是可比的。

全序集

如果对于一个集合A和关系R,满足A中的任意两个元素都是可比的,就称A和R组成全序集。

链与反链

链就是全序集,全序集就是链。

反链就是一个集合使得集合中的元素两两不可比。

Dilworth定理

偏序集中最小链划分中链的数量等于它最长反链的长度。

solution

如果两个点之间有连边,那么我们就认为这两个点是可比的。那这个题就是要求最长的反链长度。根据Dilworth定理,我们知道最长反链长度就是最小链覆盖的数量。

所以建二分图跑最大匹配即可。

注意在求链覆盖之前要先做一遍传递闭包。

然后就解决了第一问。

对于第二问,我们从右边没有被匹配的点开始dfs,从右边到左边时只走没有被匹配的边,从左到右时只走被匹配到的边。如果一个点左边没有被访问过右边被访问过,那么这个点就在所求反链里,否则不在。

对于第三问,枚举一个点,删掉这个点以及所有和他有关系的点,如果答案比之前小恰好1,那么这个点就可能出现在反链里,否则不可能。

code

/*
* @Author: wxyww
* @Date:   2020-05-27 19:57:30
* @Last Modified time: 2020-05-27 20:48:40
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
#include<cmath>
using namespace std;
typedef long long ll;
const int N = 1010;

ll read() {
	ll x = 0,f = 1;char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') f = -1; c = getchar();
	}
	while(c >= '0' && c <= '9') {
		x = x * 10 + c - '0'; c = getchar();
	}
	return x * f;
}
struct node {
	int v,nxt,w;
}e[N * N];
int S,T,head[N],ejs = 1,cur[N];
void add(int u,int v,int w) {
	e[++ejs].v = v;e[ejs].nxt = head[u];head[u] = ejs;e[ejs].w = w;
	e[++ejs].v = u;e[ejs].nxt = head[v];head[v] = ejs;e[ejs].w = 0;
}
int NOW,a[N][N],dep[N];
int n,m,vis[N];
queue<int>q;
int check(int x) {
	if(x > n) x -= n;
	return x == NOW || a[x][NOW] || a[NOW][x];
}

int bfs() {
	memset(dep,0,sizeof(dep));
	dep[S] = 1;q.push(S);
	while(!q.empty()) {
		int u = q.front();q.pop();
		for(int i = head[u];i;i = e[i].nxt) {
			int v = e[i].v;
			if(check(v)) continue;
			if(!dep[v] && e[i].w) {
				dep[v] = dep[u] + 1;q.push(v);
			}
		}
	}
	return dep[T];
}
int dfs(int u,int now) {
	if(u == T) return now;
	int ret = 0;
	for(int &i = cur[u];i;i = e[i].nxt) {
		int v = e[i].v;
		if(check(v)) continue;
		if(dep[v] == dep[u] + 1 && e[i].w) {
			int k = dfs(v,min(now - ret,e[i].w));
			e[i].w -= k;e[i ^ 1].w += k;
			ret += k;
			if(ret == now) return ret;
		}
	}
	return ret;
}
int dinic() {
	int ret = 0;
	while(bfs()) {
		memcpy(cur,head,sizeof(cur));
		ret += dfs(S,10);
	}
	return ret;
}
int get(int u) {
	vis[u] = 1;
	for(int i = head[u];i;i = e[i].nxt) {
		int v = e[i].v;
		if(!e[i].w && !vis[v]) {
			get(v);
		}
	}
}
int main() {
	// freopen("1143/5.in","r",stdin);
	n = read(),m = read();
	for(int i = 1;i <= m;++i) {
		int u = read(),v = read();
		a[u][v] = 1;
	}
	for(int k = 1;k <= n;++k)
		for(int i = 1;i <= n;++i)
			for(int j = 1;j <= n;++j)
				a[i][j] |= a[i][k] & a[k][j];
	
	S = n + n + 1,T = S + 1;
	for(int i = 1;i <= n;++i) {
		add(S,i,1);add(i + n,T,1);
		for(int j = 1;j <= n;++j) {
			if(a[i][j]) add(i,j + n,1);
		}
	}

	int ans = n - dinic();
	cout<<ans<<endl;

	for(int i = head[T];i;i = e[i].nxt) {
		if(!e[i].w)
			get(e[i].v);
	}
	for(int i = 1;i <= n;++i) {
		if(!vis[i] && vis[i + n]) printf("1");
		else printf("0");
	}
	puts("");

	for(int i = 1;i <= n;++i) {
		NOW = i;
		for(int i = 2;i <= ejs;i += 2) {
			e[i].w = 1;e[i ^ 1].w = 0;
		}
		int tot = 0;
		for(int j = 1;j <= n;++j) if(j != i && !a[j][i] && !a[i][j]) tot++;
		// cout<<tot<<endl;
		if(tot - dinic() != ans - 1) printf("0");
		else printf("1");
	}
	return 0;
}
posted @ 2020-05-27 22:06  wxyww  阅读(194)  评论(0编辑  收藏  举报