题解 [CTSC2008] 祭祀

传送门

发现就是 Dilworth 定理
但是要输出方案,还要求可行点以及方案数

首先 Dilworth 定理转化:
DAG 最大独立集 = 最小链覆盖

  • 关于 DAG 最小链覆盖:
    一个做法是拆点二分图最大匹配
    注意到每个点最多一个入度一个出度,那么就要最大化有出度的点个数
    那么将每个点拆为入点和出点,每条边连边 \((x, y')\)
    跑二分图最大匹配即可
  • 关于 DAG 最小点覆盖/最大独立集构造方案
    考虑拆点二分图最大匹配的残量网络
    一个构造是 从右侧的所有非匹配点开始 dfs,右侧点向左只能走非匹配边,左侧点向右只能走匹配边
    那么被 dfs 到的左侧点和未被 dfs 到的右侧点构成了一组最小点覆盖
    证明的话基本是反证,若有边未被覆盖则左侧点未被 dfs 且右侧点被 dfs
    然而右侧的所有匹配点都是从左边 dfs 过来的,所以不可能
  • 关于 DAG 最小点覆盖/最大独立集可行点/方案数
    Sol 1:
    以最大独立集为例,考虑枚举每个点 check 是否可行
    那么强制选择这个点,因为是最长反链所以所有和这个点有偏序关系的点都不能选
    对剩下的边和点求最大独立集,若最大独立集只减小了 1 则是可行点
    Sol 2(参考 @Piwry 的做法):
    考虑所有可行点,我们将所有有偏序关系的可行点染成同一种颜色
    因为是可行点,所以这个染色不存在传递性
    (即不存在由 \(A\) 染到 \(B\),又由 \(B\) 染到 \(C\),但 \(A\) 不能染到 \(C\) 的情况,否则最长反链中加入 \(A\)\(C\) 更优)
    那么不同颜色的点之间就是独立的,只要最后每种颜色的点恰好选了一个即可
    那么方案及方案数都是容易求的了

复杂度 \(O(n^2m\sqrt n)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;	
#define INF 0x3f3f3f3f
#define N 2010
#define pb push_back
#define ll long long
#define ull unsigned long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m;
bitset<N> mask[N];
bool vis[N], del[N];
vector<int> to[N], sta;
int head[N], dep[N], cur[N], ecnt=1, s, t, ans, tot;
struct edge{int to, next, val;}e[N*N*2];
inline void add(int s, int t, int w) {e[++ecnt]={t, head[s], w}; head[s]=ecnt;}

bool bfs(int s, int t) {
	for (int i=1; i<=tot; ++i) dep[i]=0;
	queue<int> q;
	dep[s]=1; cur[s]=head[s];
	q.push(s);
	while (q.size()) {
		int u=q.front(); q.pop();
		for (int i=head[u],v; ~i; i=e[i].next) {
			v = e[i].to;
			if (!del[v]&&e[i].val&&!dep[v]) {
				dep[v]=dep[u]+1;
				cur[v]=head[v];
				if (v==t) return 1;
				q.push(v);
			}
		}
	}
	return 0;
}

int dfs(int u, int in) {
	if (u==t||!in) return in;
	int rest=in, tem;
	for (int i=cur[u],v; ~i; cur[u]=i=e[i].next) {
		v = e[i].to;
		if (!del[v]&&e[i].val&&dep[v]==dep[u]+1) {
			tem=dfs(v, min(rest, e[i].val));
			if (!tem) dep[v]=0;
			rest-=tem;
			e[i].val-=tem;
			e[i^1].val+=tem;
			if (!rest) break;
		}
	}
	return in-rest;
}

void dfs(int u) {
	vis[u]=1;
	for (int i=head[u],v; ~i; i=e[i].next) {
		v = e[i].to;
		if (v!=s&&v!=t&&!e[i].val&&!vis[v]) dfs(v);
	}
}

signed main()
{
	n=read(); m=read();
	s=2*n+1, t=2*n+2, tot=2*n+2;
	memset(head, -1, sizeof(head));
	for (int i=1,u,v; i<=m; ++i) {
		u=read(); v=read();
		mask[u][v]=1;
	}
	for (int k=1; k<=n; ++k)
		for (int i=1; i<=n; ++i) if (mask[i][k])
			mask[i]|=mask[k];
	for (int i=1; i<=n; ++i)
		for (int j=1; j<=n; ++j) if (mask[i][j])
			to[i].pb(j); //, cout<<"link: "<<i<<' '<<j<<endl;
	for (int u=1; u<=n; ++u)
		for (auto v:to[u])
			add(u, v+n, 1), add(v+n, u, 0);
	for (int i=1; i<=n; ++i) {
		add(s, i, 1), add(i, s, 0);
		add(i+n, t, 1), add(t, i+n, 0);
	}
	while (bfs(s, t)) ans+=dfs(s, INF);
	printf("%d\n", n-ans);
	for (int u=n+1; u<=2*n; ++u)
		for (int i=head[u]; ~i; i=e[i].next)
			if (e[i].to==t && e[i].val)
				dfs(u);
	for (int i=1; i<=n; ++i)
		if (!(vis[i]||!vis[i+n])) vis[i]=1;
		else vis[i]=0;
	for (int i=1; i<=n; ++i) printf("%d", vis[i]);
	printf("\n");
	for (int i=1,ans,siz; i<=n; ++i) {
		// if (vis[i]) {putchar('1'); continue;}
		for (int i=2; i<=ecnt; i+=2) e[i].val+=e[i^1].val, e[i^1].val=0;
		del[i]=del[i+n]=1; ans=0; siz=n-1;
		assert(!mask[i][i]);
		for (int j=1; j<=n; ++j) if (mask[i][j]||mask[j][i]) del[j]=del[j+n]=1, --siz;
		while (bfs(s, t)) ans+=dfs(s, INF);
		// cout<<siz-ans<<endl;
		putchar(siz-ans==n-::ans-1?'1':'0');
		for (int j=1; j<=2*n; ++j) del[j]=0;
	}
	printf("\n");

	return 0;
}
posted @ 2022-06-13 20:45  Administrator-09  阅读(3)  评论(0编辑  收藏  举报