Solution [CTSC2008]祭祀
题目大意:求有向图最长反链,输出一种合法方案,以及一个点是否出现在至少一种合法方案里面
二分图
分析:
最长反链不好求,做个(Floyd)传递闭包,然后就是求最大点独立集了
然后(3min)淦了(CTSC)题?naive,毒瘤SPJ让你输出方案
第二问我们先找一个最大匹配,从左侧点的选没有被匹配的跑(dfs),每次从左到有走非匹配边,从右往左走匹配边。然后一路走一路标记,左边没有被标记过的点和右边被标记过的点就是最大点独立集
证明显然,匈牙利树不好想可以转成网络流
然后第三问,我们依次枚举删掉每一个点,如果删掉这个点之后答案减小了(1)那么这个点可能在最长反链中否则不可能
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 256,maxm = maxn * maxn;
vector<int> G[maxn];
inline void addedge(int from,int to){G[from].push_back(to);}
int n,m,ans,ban[maxn],match[maxn],vis[maxn],f[maxn][maxn],left[maxn],right[maxn];
inline bool find(int u){
if(ban[u])return false;
for(int v : G[u])
if(!ban[v] && !vis[v]){
vis[v] = 1;
if(!match[v] || find(match[v])){
match[v] = u;
return true;
}
}
return false;
}
inline void floyd(){
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][k] && f[k][j]);
}
inline void build(){
for(int u = 1;u <= n;u++)
for(int v = 1;v <= n;v++)
if(f[u][v])addedge(u,v);
}
inline void dfs(int u){
if(left[u])return;
left[u] = 1;
for(int v : G[u])
if(!right[v])right[v] = 1,dfs(match[v]);
}
int main(){
scanf("%d %d",&n,&m);ans = n;
for(int u,v,i = 1;i <= m;i++)
scanf("%d %d",&u,&v),f[u][v] = 1;
floyd();
build();
for(int i = 1;i <= n;i++)
memset(vis,0,sizeof(vis)),ans -= find(i);
printf("%d
",ans);
memset(vis,0,sizeof(vis));
for(int i = 1;i <= n;i++)
vis[match[i]] = 1;
for(int i = 1;i <= n;i++)
if(!vis[i])dfs(i);
for(int i = 1;i <= n;i++)
printf("%d",left[i] && !right[i]);
printf("
");
for(int gg = 1;gg <= n;gg++){
memset(match,0,sizeof(match));
memset(ban,0,sizeof(ban));
int tmp = 0;
for(int i = 1;i <= n;i++)
if(f[i][gg] || f[gg][i] || i == gg)ban[i] = 1;
else tmp++;
for(int i = 1;i <= n;i++)
memset(vis,0,sizeof(vis)),tmp -= find(i);
printf("%d",tmp == ans - 1);
}
return 0;
}