[CTSC2008]祭祀
[CTSC2008]祭祀
0x01 题意
给出一个DAG,求出这个图的最长反链,并构造方案,以及在反链最长情况下每个点是否能选(有spj)
0x02 解
最长反链:一张有向无环图的最长反链为一个集合\(S\subseteq V\),满足对于\(S\)中的任意两个不同的点\(u,v\in S\,(u\neq 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('\n');
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;
}