题解 [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;
}