题意是裸的二分图关键点(必然在二分图最大匹配中出现的点).比较经典的做法在cyb15年的论文里有:

前几天写jzoj5007的时候脑补了一种基于最小割可行边的做法:考虑用最大流求解二分图匹配.如果某个点必须在最大匹配中出现,相当于删去这个点后最大匹配数目减少,即删去这个点和源/汇点的连边后剩下的网络中s到t的最小割减小.反之,如果删去这个点后最大匹配数目不变,那么删去这个点和源/汇的连边后网络的最小割不变.上述两个条件分别等价于是否存在一个原网络的最小割方案包含这个点和源/汇的连边,于是转化成网络流中的最小割可行边问题,可以通过在残量网络上用tarjan求SCC解决,即将残量网络上所有流量不为零的正向边和反向边取出,按照边原先的指向建图,求SCC,如果某条满流边连接的两个点在不同的强联通分量中说明这条边是最小割的可行边.这个做法写起来比较麻烦,不是很优越.

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=,maxm=;
struct edge{
int to,next,w;
}lst[maxm],lst2[maxm];int len=,first[maxn],_first[maxn],first2[maxn],len2=;
void addedge(int a,int b,int w){
lst[len].to=b;lst[len].next=first[a];lst[len].w=w;first[a]=len++;
lst[len].to=a;lst[len].next=first[b];lst[len].w=;first[b]=len++;
}
void addedge2(int a,int b){
lst2[len2].to=b;lst2[len2].next=first2[a];first2[a]=len2++;
// lst2[len2].to=a;lst2[len2].next=first2[b];lst2[len2].w=w;first2[b]=len2++;
}
int n1,n2,m;
int q[maxn],vis[maxn],dis[maxn],s,t,head,tail,T;
bool bfs(){
head=tail=;vis[s]=++T;dis[s]=;q[tail++]=s;
while(head!=tail){
int x=q[head++];
for(int pt=first[x];pt!=-;pt=lst[pt].next){
if(lst[pt].w&&vis[lst[pt].to]!=T){
dis[lst[pt].to]=dis[x]+;vis[lst[pt].to]=T;q[tail++]=lst[pt].to;
}
}
}
if(vis[t]==T)memcpy(_first,first,sizeof(first));
return vis[t]==T;
}
int dfs(int x,int lim){
if(x==t)return lim;
int flow=,a;
for(int pt=_first[x];pt!=-;pt=lst[pt].next){
_first[x]=pt;
if(lst[pt].w&&dis[lst[pt].to]==dis[x]+&&(a=dfs(lst[pt].to,min(lst[pt].w,lim-flow)))){
lst[pt].w-=a;lst[pt^].w+=a;flow+=a;
if(lim==flow)return flow;
}
}
return flow;
}
int dinic(){
int ans=,x;
while(bfs())while(x=dfs(s,0x7f7f7f7f))ans+=x;
return ans;
}
int used[maxn];
int ans1[maxn],ans2[maxn];
int dfn[maxn],clk;
void Dfs(int x,int typ){
dfn[x]=clk;
for(int pt=first[x];pt!=-;pt=lst[pt].next){
if(lst[pt].w==typ&&dfn[lst[pt].to]!=clk)Dfs(lst[pt].to,typ);
}
}
int main(){
memset(first,-,sizeof(first));
scanf("%d%d%d",&n1,&n2,&m);
int a,b;
s=;t=n1+n2+;
for(int i=;i<=n1;++i)addedge(s,i,);
for(int i=;i<=n2;++i)addedge(n1+i,t,);
for(int i=;i<=m;++i){
scanf("%d%d",&a,&b);addedge(a,n1+b,);
}
dinic();
for(int pt=first[s];pt!=-;pt=lst[pt].next){
if(lst[pt].w==)used[lst[pt].to]=;
}
for(int pt=first[t];pt!=-;pt=lst[pt].next){
if(lst[pt].w==)used[lst[pt].to]=;
}
++clk;
for(int i=;i<=n1;++i){
if(!used[i])Dfs(i,);
}
for(int i=;i<=n1;++i){
if(used[i]&&dfn[i]!=clk)ans1[i]=;
}
++clk;
for(int i=;i<=n2;++i){
if(!used[n1+i])Dfs(n1+i,);
}
for(int i=n1+;i<=n1+n2;++i){
if(used[i]&&dfn[i]!=clk)ans2[i]=;
}
for(int i=;i<=n1;++i)if(ans1[i])printf("%d\n",i);
for(int i=;i<=n2;++i)if(ans2[i+n1])printf("%d\n",i);
return ;
}
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=,maxm=;
struct edge{
int to,next,w;
}lst[maxm],lst2[maxm];int len=,first[maxn],_first[maxn],first2[maxn],len2=;
void addedge(int a,int b,int w){
lst[len].to=b;lst[len].next=first[a];lst[len].w=w;first[a]=len++;
lst[len].to=a;lst[len].next=first[b];lst[len].w=;first[b]=len++;
}
void addedge2(int a,int b){
lst2[len2].to=b;lst2[len2].next=first2[a];first2[a]=len2++;
// lst2[len2].to=a;lst2[len2].next=first2[b];lst2[len2].w=w;first2[b]=len2++;
}
int n1,n2,m;
int q[maxn],vis[maxn],dis[maxn],s,t,head,tail,T;
bool bfs(){
head=tail=;vis[s]=++T;dis[s]=;q[tail++]=s;
while(head!=tail){
int x=q[head++];
for(int pt=first[x];pt!=-;pt=lst[pt].next){
if(lst[pt].w&&vis[lst[pt].to]!=T){
dis[lst[pt].to]=dis[x]+;vis[lst[pt].to]=T;q[tail++]=lst[pt].to;
}
}
}
if(vis[t]==T)memcpy(_first,first,sizeof(first));
return vis[t]==T;
}
int dfs(int x,int lim){
if(x==t)return lim;
int flow=,a;
for(int pt=_first[x];pt!=-;pt=lst[pt].next){
_first[x]=pt;
if(lst[pt].w&&dis[lst[pt].to]==dis[x]+&&(a=dfs(lst[pt].to,min(lst[pt].w,lim-flow)))){
lst[pt].w-=a;lst[pt^].w+=a;flow+=a;
if(lim==flow)return flow;
}
}
return flow;
}
int dinic(){
int ans=,x;
while(bfs())while(x=dfs(s,0x7f7f7f7f))ans+=x;
return ans;
}
int ans1[maxn],ans2[maxn];
namespace Tarjan{
int stack[maxn],top,dfn[maxn],low[maxn],T,belong[maxn],tot;
bool ins[maxn];
void dfs(int x){
low[x]=dfn[x]=++T;ins[x]=true;stack[top++]=x;
for(int pt=first2[x];pt;pt=lst2[pt].next){
if(!dfn[lst2[pt].to]){
dfs(lst2[pt].to);
if(low[lst2[pt].to]<low[x])low[x]=low[lst2[pt].to];
}else if(ins[lst2[pt].to]&&dfn[lst2[pt].to]<low[x])low[x]=dfn[lst2[pt].to];
}
if(dfn[x]==low[x]){
++tot;
do{
ins[stack[--top]]=false;
belong[stack[top]]=tot;
}while(stack[top]!=x);
}
}
void tarjan(){
for(int i=s;i<=t;++i){
if(!dfn[i])dfs(i);
}
}
};
int main(){
memset(first,-,sizeof(first));
scanf("%d%d%d",&n1,&n2,&m);
int a,b;
s=;t=n1+n2+;
for(int i=;i<=n1;++i)addedge(s,i,);
for(int i=;i<=n2;++i)addedge(n1+i,t,);
for(int i=;i<=m;++i){
scanf("%d%d",&a,&b);addedge(a,n1+b,);
}
dinic();
for(int i=s;i<=t;++i){
for(int pt=first[i];pt!=-;pt=lst[pt].next){
if(lst[pt].w){
if(lst[pt^].w)addedge2(i,lst[pt].to);
else addedge2(i,lst[pt].to);
}
}
}
Tarjan::tarjan();
using Tarjan::belong;
for(int i=s;i<=t;++i){
for(int pt=first[i];pt!=-;pt=lst[pt].next){
if(lst[pt].w==&&belong[i]!=belong[lst[pt].to]){
if(i==s&&lst[pt].to<=n1){
ans1[lst[pt].to]=;
}
if(lst[pt].to==t&&i>n1){
ans2[i-n1]=;
}
}
}
}
for(int i=;i<=n1;++i)if(ans1[i])printf("%d\n",i);
for(int i=;i<=n2;++i)if(ans2[i])printf("%d\n",i);
return ;
}

bzoj3546[ONTAK2010]Life of the Party的更多相关文章

  1. 【BZOJ】【3550】【ONTAK2010】Vacation

    网络流/费用流 Orz太神犇了这题…… 我一开始想成跟Intervals那题一样了……每个数a[i]相当于覆盖了(a[i]-n,a[i]+n)这个区间……但是这样是错的!!随便就找出反例了……我居然还 ...

  2. BZOJ3550: [ONTAK2010]Vacation

    3550: [ONTAK2010]Vacation Time Limit: 10 Sec  Memory Limit: 96 MBSubmit: 91  Solved: 71[Submit][Stat ...

  3. bzoj 3545&&3551: [ONTAK2010]Peaks &&加强版 平衡树&&并查集合并树&&主席树

    3545: [ONTAK2010]Peaks Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 635  Solved: 177[Submit][Stat ...

  4. BZOJ 3544: [ONTAK2010]Creative Accounting( BST )

    题意 : 一段序列 , 求一段子序列和取余 M 的最大值 其实是一道水题... 前缀和 , 然后就是找 ( sum( r ) - sum( l ) ) % M 的最大值 . 考虑一个 sum( r ) ...

  5. BZOJ 3545: [ONTAK2010]Peaks( BST + 启发式合并 + 并查集 )

    这道题很好想, 离线, 按询问的x排序从小到大, 然后用并查集维护连通性, 用平衡树维护连通块的山的权值, 合并就用启发式合并.时间复杂度的话, 排序是O(mlogm + qlogq), 启发式合并是 ...

  6. BZOJ 3551: [ONTAK2010]Peaks加强版 [Kruskal重构树 dfs序 主席树]

    3551: [ONTAK2010]Peaks加强版 题意:带权图,多组询问与一个点通过边权\(\le lim\)的边连通的点中点权k大值,强制在线 PoPoQQQ大爷题解传送门 说一下感受: 容易发现 ...

  7. BZOJ 3545: [ONTAK2010]Peaks [Splay启发式合并]

    3545: [ONTAK2010]Peaks 题意:带权图,多组询问与一个点通过边权\(\le x\)的边连通的点中点权k大值 又读错题了,输出点一直WA,问的是点权啊 本题加强版强制在线了,那这道题 ...

  8. bzoj3545: [ONTAK2010]Peaks 重构树 主席树

    题目链接 bzoj3545: [ONTAK2010]Peaks 题解 套路重构树上主席树 代码 #include<cstdio> #include<algorithm> #de ...

  9. 【BZOJ3545】 [ONTAK2010]Peaks

    BZOJ3545 [ONTAK2010]Peaks Solution 既然会加强版,直接把强制在线的操作去掉就好了. 代码实现 #include<stdio.h> #include< ...

随机推荐

  1. 20145234黄斐《Java程序设计》第八周学习总结

    教材学习内容总结 第十五章部分 - 通用API 通用API 日志: 日志对信息安全意义重大,审计.取证.入侵检测等都会用到日志信息 日志API Logger: 注意无法使用构造方法生成Logger对象 ...

  2. 北京Uber优步司机奖励政策(1月24日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  3. 苏州Uber优步司机奖励政策(8月31日至9月6日)

    当周最新司机奖励(8月31日至9月6日) 滴滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http: ...

  4. C++三元操作符

    c++的三元操作符形式: //条件表达式 ? 表达式1 : 表达式2; 语义:如果“条件表达式”为true,则整个表达式的值就是表达式1,忽略表达式2:如果“条件表达式”为false,则整个表达式的值 ...

  5. 4 进程间通信Queue [kjuː]

    1.进程间通信-Queue Process之间有时需要通信,操作系统提供了很多机制来实现进程间的通信. 说明 初始化Queue()对象时(例如:q=Queue()),若括号中没有指定最大可接收的消息数 ...

  6. clr via c#读书笔记四:call、callvirt

    1.嵌套类,就是定义在类中的类:嵌套类可以访问外部类的方法.属性.字段而不管访问修饰符的限制,但是外部类只能够访问修饰符为public.internal的嵌套类的字段.方法.属性: 2.CLR如何调用 ...

  7. SpringBoot-05:SpringBoot初运行以及tomcat端口号的修改

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 上篇博客讲了,如何创建SpringBoot工程,所以,我本篇博客讲述,如何跑起来自己的第一个案例 1.准备一个 ...

  8. Linux命令应用大词典-第34章 打印与传真

    34.1 lpr:打印文件 34.2 lpq:显示打印队列状态 34.3 lprm:取消打印作业 34.4 lpstat:显示cups状态信息 34.5 cupsaccept:接受作业发送到目的地 3 ...

  9. 第一阶段·Linux运维基础-第2章·Linux系统目录结构介绍

    01 变量与PS1 02 添加用户 03 关闭SELinux 04 关闭iptables 05 显示中文乱码排查过程 06 总结 07 目录结构课程内容 08 Linux目录结构特点 09 Linux ...

  10. 【转】MMORPG游戏服务器技能系统设计:表格字段与技能程序框架

    本文主要从一个程序员的角度阐述一下mmorpg服务器技能系统的程序框架设计,最近在做这个,就当做一个总结吧,其中某些概念可能没有解释清楚,欢迎大家拍砖讨论~ 技能其实是战斗系统的一个组成部分,战斗基本 ...