目录
显示
题目链接
https://www.luogu.org/problem/P4298
题解
Part 1
我们定义反链为一个点的集合,集合内的任意两点不能存在一条路径相连。
则可以证明:最长反链=最小链覆盖=点数-最大匹配数。
先跑一遍传递闭包,我们将点 \(i\) 拆成两个点 \(i,i+n\),对于 \(x\) 能到达 \(y\) 的情况,连接一条 \(x\) 到 \(y+n\) 的路径。
然后跑一遍二分图最大匹配即可。
Part 2
第二问让我们构造一组二分图的最大独立集。
我们可以这样构造:每次从二分图左边的找一个没匹配的点,从左往右走的时候走匹配边,从右往左走的时候走非匹配边即可。
最后左边未访问的点和右边访问的点即为一组最大独立集。
r_64 在 uoj 上给出了该构造正确性的 证明 。
Part 3
第三问让我们求出哪些点可以成为最长反链的组成部分。
我们每次枚举一个点,删除它及与它相连的所有边,再求一次最长反链,如果最长反链变小,则该点是最长反链的一个组成部分。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define INF 0x3f3f3f3f
using namespace std;
struct edge
{
int v,w,next;
}e[2000005];
int f[105][105],head[205],vis[205],cur[205],dep[205],s,t,cnt=1,n,m;
void addedge(int u,int v,int w)
{
e[++cnt].v=v;
e[cnt].w=w;
e[cnt].next=head[u];
head[u]=cnt;
}
bool bfs()
{
queue<int> q;
memcpy(cur,head,sizeof(cur));
memset(dep,INF,sizeof(dep));
dep[s]=0,vis[s]=1;
q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i;i=e[i].next)
if(e[i].w&&dep[e[i].v]>dep[u]+1)
{
dep[e[i].v]=dep[u]+1;
if(!vis[e[i].v])
{
q.push(e[i].v);
vis[e[i].v]=1;
}
}
}
return dep[t]!=INF;
}
int dfs(int u,int w)
{
if(u==t)return w;
int used=0;
for(int i=cur[u];i;i=e[i].next)
{
cur[u]=i;
if(dep[e[i].v]==dep[u]+1&&e[i].w)
{
int flow=dfs(e[i].v,min(w,e[i].w));
if(flow)
{
used+=flow;
e[i].w-=flow;
e[i^1].w+=flow;
if(used==w)break;
}
}
}
return used;
}
int res[205];
void find(int u)
{
if(!res[u]&&u<=n)return;
if(res[u]&&u>n)return;
if(u<=n)
{
res[u]=0;
for(int i=head[u];i;i=e[i].next)
if(e[i].w)find(e[i].v);
}
else
{
res[u]=1;
for(int i=head[u];i;i=e[i].next)
if(!e[i^1].w)find(e[i].v);
}
}
int main()
{
//Initiation start
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
f[u][v]=1;
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[i][j]=f[i][j]||(f[i][k]&&f[k][j]);
//Initiation end
//Part 1 start
s=2*n+1,t=2*n+2;
for(int i=1;i<=n;i++)
{
addedge(s,i,1);
addedge(i,s,0);
addedge(i+n,t,1);
addedge(t,i+n,0);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i!=j&&f[i][j])
{
addedge(i,j+n,1);
addedge(j+n,i,0);
}
int ans=0;
while(bfs())
ans+=dfs(s,INF);
printf("%d\n",ans=n-ans);
//Part 1 end
//Part 2 start
for(int i=1;i<=n;i++)
res[i]=1;
for(int i=1;i<=n;i++)
if(e[4*i-2].w)find(i);
for(int i=1;i<=n;i++)
printf("%d",!(res[i]|res[i+n]));
puts("");
//Part 2 end
//Part 3 start
for(int i=1;i<=n;i++)
{
memset(head,0,sizeof(head));
cnt=1;
int cntp=0;
for(int j=1;j<=n;j++)
if(i!=j&&(!f[i][j])&&(!f[j][i]))
{
addedge(s,j,1);
addedge(j,s,0);
addedge(j+n,t,1);
addedge(t,j+n,0);
cntp++;
}
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
if(j!=k&&i!=j&&f[j][k])
{
addedge(j,k+n,1);
addedge(k+n,j,0);
}
while(bfs())
cntp-=dfs(s,INF);
printf("%d",ans-1==cntp);
}
//Part 3 end
return 0;
}