P4298题解

[CTSC2008] 祭祀

前言

我需要抨击mx题单设定人在C班(冲刺NOIP \(200+\))包括但不限于:
1.讲插头dp
2.树链剖分题单中给出不知hyw的动态淀粉质
3.在图论题单里面放入只用矩阵加速的题目
4.加入dliworth与二分图最大匹配(本文主角)

我还需要抨击mxC班每次的榜一(包括我自己在内)所有人的卷题行为,已经不是人类了。本题我在还剩下13个题没写的情况下借助xft题解鏖战1.5h终于战胜。本题解 全部 部分借鉴xft

题目简述

分三问:

1.求DAG上的最长反链长度
2.构造出最长反链情况
3.最长反链情况下每个点能否选

题目分析

先处理第一个问题,直接根据dliworth定理,最长反链长度等于最小链覆盖,我们可以考虑把DAG上每个点拆成一个前驱和一个后继,并先考虑把每个点当成一条链,于是只要我们合并两条链,就可以减少一条链,而合并两条链相当于匹配一条链上结尾的点的后继和另一条链上开头的前驱。于是我们就可以把前驱和后继分成两组形成一个二分图,每匹配一次就会少一条链,最大匹配就会得到最小链覆盖。

注:上述链是序理论中的链,相当于一个在一条路径上的点集(不一定连续)

再看第二问,考虑一个引理(证明看xft的blog):令最大独立集为 \(I\),考虑选出所有前驱和后继都属于 \(I\) 的点会形成最长反链。

于是我们就可以通过dfs最小点覆盖得到最大独立集,只要那个点前驱未被选且后继被选即可。

最后是第三问,我们可以考虑保证这个点被选,于是与它相连的点都不能选,我们考虑先把这些点都ban掉,再把这个点去掉,然后因为如果这个点在最长反链里面,去掉它最长反链长度会减一,所以判断新的最长反链长度是否会减一就可以了。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1000+5;
int mat[N],to[N];//前驱后继
int n,m;
int e[N][N];//新图
int ban[N];//删边
int vis[N],now;
bool find(int x)//匈牙利
{
    if(ban[x])return 0;
    for(int i=1;i<=n;i++)
    {
        if(e[x][i]&&vis[i]!=now)
        {
            if(ban[i])continue;
            vis[i]=now;
            if(mat[i]==0||find(mat[i]))
            {
                mat[i]=x;
                to[x]=i;
                return 1;
            }
        }
    }
    return 0;
}
int s[N],t[N];//左右选点
void dfs(int x)
{
    if(s[x])return;
    s[x]=1;
    for(int i=1;i<=n;i++)
    {
        if(e[x][i]&&!t[i])
        {
            t[i]=1;
            dfs(mat[i]);
        }
    }
}//类最小点覆盖
int mp[N][N],ans;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int x,y;
        cin>>x>>y;
        mp[x][y]=1;
    }
    for(int k=1;k<=n;k++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)mp[i][j]|=mp[i][k]&mp[k][j];//传递闭包
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)e[i][j]=mp[i][j];//传到新
    ans=n;
    for(now=1;now<=n;now++)ans-=find(now);//用最大匹配求最小链覆盖
    cout<<ans<<'\n';
    for(int i=1;i<=n;i++)if(!to[i])dfs(i);//从右边未选点dfs
    for(int i=1;i<=n;i++)cout<<(s[i]&&!t[i]);//最长反链
    cout<<'\n';
    for(int i=1;i<=n;i++)
    {
        memset(e,0,sizeof(e));
        memset(to,0,sizeof(to));
        memset(ban,0,sizeof(ban));
        memset(mat,0,sizeof(mat));
        memset(vis,0,sizeof(vis));
        now=0;
        int res=0;
        for(int j=1;j<=n;j++)
        {
            if(mp[j][i]||mp[i][j]||i==j)ban[j]=1;
            else res++;
        }
        for(int j=1;j<=n;j++)for(int k=1;k<=n;k++)if(!ban[j]&&!ban[k])e[j][k]=mp[j][k];
        for(int j=1;j<=n;j++)
            if(ban[j]==0)
            {
                now++;
                res-=find(j);
            }
        cout<<(res==ans-1);
    }
}
posted @ 2026-07-29 17:22  tidewave  阅读(19)  评论(0)    收藏  举报