题解:P10940 舞动的夜晚
本题让我们求二分图的不可行边。
先跑一遍 dinic 求出二分图最大匹配,接下来考虑必须边(一定存在于最大匹配中的边)、可行边(有可能存在于最大匹配中的边)和不可行边(不能存在于最大匹配中的边,这道题的答案,可行边的补集)的判断条件。
如果匹配边从右向左建有向边,非匹配边从左向右建匹配边,我们可以发现,
必须边
若
可行边
若
若
容易发现必须边是可行边的子集。
不可行边
可行边的补集。
关于判断
故本题流程如下:
- dinic 求最大匹配,建立流量为
1 的边(符合上述有向图定义)。 - 在这张图上作 Tarjan 求 SCC。
- 枚举所有边,找出不可行边。
- 输出这些边的输入编号。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e4+10;
const int inf=0x3f3f3f3f;
int n,m,t,cur[MAXN<<1],dep[MAXN<<1];
int S,T;
int dfn[MAXN<<1],low[MAXN<<1],dfscnt,id[MAXN<<1],sc;
bool vis[MAXN<<1];
stack<int> st;
struct edge{
int to,w,id;
};
vector<edge> e[MAXN<<1];
vector<int> new_e[MAXN<<1];
bool bfs(){
memset(dep,-1,sizeof dep);
queue<int> q;
q.push(S);
dep[S]=0;
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=0;i<e[u].size();i++){
int v=e[u][i].to,w=e[u][i].w;
if(dep[v]==-1&&w){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[T]!=-1;
}
int dfs(int u,int limit){
if(u==T)return limit;
for(int i=cur[u];i<e[u].size();i++){
cur[u]=i;
int v=e[u][i].to,w=e[u][i].w;
if(dep[v]==dep[u]+1&&w){
int ret=dfs(v,min(w,limit));
if(ret){
e[u][i].w-=ret;
e[v][e[u][i].id].w+=ret;
return ret;
}else dep[v]=-1;
}
}
return 0;
}
void add(int u,int v){ // 神秘vector做法
int ui=e[u].size();
int vi=e[v].size();
e[u].push_back({v,1,vi});
e[v].push_back({u,0,ui});
}
void tarjan(int u){
dfn[u]=low[u]=++dfscnt;
vis[u]=1;
st.push(u);
for(int v:new_e[u]){
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}else if(vis[v]){
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u]){
int x;
sc++;
do{
x=st.top();
st.pop();
vis[x]=0;
id[x]=sc;
}while(x!=u);
}
}
map<int,int> ma[MAXN];
vector<int> _ans;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m>>t;
S=0,T=n+m+1;
for(int i=1,x,y;i<=t;i++){
cin>>x>>y;y+=n;
add(x,y);
ma[x][y]=i;
}
for(int i=1;i<=n;i++)add(S,i);
for(int i=n+1;i<=n+m;i++)add(i,T);
int ans=0,flow;
while(bfs()){ // dinic
memset(cur,0,sizeof cur);
while(flow=dfs(S,inf))ans+=flow;
}
for(int u=0;u<=T;u++){
for(auto [v,w,id]:e[u]){
if(w>0)new_e[u].push_back(v); // 建立流量为1的边
if(e[v][id].w>0)new_e[v].push_back(u);
}
}
for(int i=1;i<=n+m;i++) // 求SCC
if(!dfn[i])tarjan(i);
for(int u=1;u<=n;u++)
for(auto [v,w,pp]:e[u])
if(w!=0&&id[u]!=id[v]){ // 不是匹配边且u,v不在一个SCC
if(v==0)continue;
_ans.push_back(ma[u][v]);
}
cout<<_ans.size()<<'\n';
sort(_ans.begin(),_ans.end());
for(int i:_ans)cout<<i<<' ';
cout<<'\n';
return 0;
}