[CTSC2008]祭祀

[CTSC2008]祭祀

0x01 题意

给出一个DAG,求出这个图的最长反链,并构造方案,以及在反链最长情况下每个点是否能选(有spj)

0x02 解

最长反链:一张有向无环图的最长反链为一个集合(Ssubseteq V),满足对于(S)中的任意两个不同的点(u,vin S\,(u eq v))(u)不能到达(v)(v)也不能到达(u),且(S)的大小尽量大。

第一问:这里有一波二分图整理,记住就行,最长反链=最小链覆盖,而最小链覆盖和最小路径覆盖可以通过传递闭包进行互化,所以反链长度很好求

第二问:

结论1:求传递闭包拆点后二分图的最大匹配,从二分图的左部分的非匹配点开始DFS,左边的点只能走非匹配边向右边访问,右边的点只能走匹配边向左边访问,取左边没DFS到的点以及右边DFS到的点组成一个集合,该集合是二分图的一个最小点覆盖

结论2:求二分图的最大独立集(最小点覆盖的补集),取对应在二分图里的两个点都在最大独立集里的岔口组成一个集合,该集合为一个最长反链方案

无力证明,小粉兔

第三问:

考虑减法原理,删去一个点集对答案的影响,若该点可以选,则删去该点对答案的影响是减一,若该点不能选,对答案的影响是减去一个大于一的数,因为反链中的点互相不能到达,不能作反链点的点一定可以到达反链中的至少两个点

0x03 码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#include<deque>
#include<set>
#include<stack>
#include<bitset>
#include<cstring>
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
using namespace std;
const int INF=0x3f3f3f3f,N=110,M=1010;

int mapp[N][N],n,m,lx[N];

inline int read(){
    int x=0,y=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') y=-1;c=getchar();}
    while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*y;
}

void cdbb(){
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                mapp[j][k]|=mapp[j][i]&mapp[i][k];
}

int match[N],isdel[N],flg[2][N],res;
bool st[N];

bool find(int x){
	for(int i=1;i<=n;i++){
		if(mapp[x][i]==0||isdel[i]) continue;
        if(!st[i]){
			st[i]=true;
            if(match[i]==0||find(match[i])){
				match[i]=x;
                return true;
            }
        }
    }
    
    return false;
}

int work(){
    int res=0;
    for(int i=1;i<=n;i++){
        memset(st,false,sizeof st);
        if(find(i)) res++;
    }
    return res;
}

void dfs(int x){
    flg[1][x]=1;
    for(int i=1;i<=n;i++) if(mapp[x][i]&&match[i]!=x&&!flg[0][i]){
        flg[0][i]=1;
        if(match[i]!=0) dfs(match[i]);
    }
}

void gz(){
    for(int i=1;i<=n;i++) lx[match[i]]=1;
    for(int i=1;i<=n;i++) if(!lx[i]) dfs(i);
    for(int i=1;i<=n;i++) if(!flg[1][i]||flg[0][i]) putchar('0');else putchar('1');

    putchar('
');
    for(int rt=1;rt<=n;rt++){
        memset(isdel,0,sizeof isdel);
        memset(match,0,sizeof match);
        int cnt=0,nn=n;
        for(int i=1;i<=n;i++) if(mapp[i][rt]||mapp[rt][i]||i==rt) isdel[i]=1,--nn;
        for(int i=1;i<=n;i++){
            if(isdel[i]) continue;
            memset(st,0,sizeof st);
            if(find(i)) ++cnt;
        }
        printf("%d",(nn-cnt==(n-res)-1));
    }
}

int main(){
    
    memset(mapp,0,sizeof(mapp));
    n=read(),m=read();
    for(int i=1;i<=m;i++){
        int x,y;
        x=read(),y=read();
        mapp[x][y]=1;
    }
    cdbb();

    res=work();
    cout<<n-res<<endl;


    gz();


    return 0;
}
原文地址:https://www.cnblogs.com/wsyunine/p/14732669.html