【最小割】AGC038F
作者:互联网
这是一个神仙网络流QAQ
首先这个问题可以转化成在满足题目限制的情况下,求最少的\(A_i=B_i\)的\(i\)的个数
你把\(i\rightarrow p_i\)和\(i\rightarrow q_i\)连出两张图
然后你发现,在\(i\rightarrow p_i\)上成环的\(i\),\(A_i\)选择的状态相同(\(B_i\)同理)
然后分类讨论一下:
\(P_i=Q_i=i\)
此时不管怎么选\(A_i\)都等于\(B_i\)
\(P_i=i\),\(Q_i\neq i\)
选\(B_i=i\)时\(A_i\)会等于\(B_i\)
\(P_i\neq i\),\(Q_i=i\)
选\(A_i=i\)时\(A_i\)会等于\(B_i\)
\(P_i\neq Q_i\neq i\)
选\(A_i=i\),\(B_i=i\)时\(A_i\)会等于\(B_i\)
\(P_i=Q_i\neq i\)
选\(A_i=i,B_i=i\)或\(A_i=P_i,B_i=Q_i\)时\(A_i=B_i\)
然后你发现第五个限制有点难搞,但是可以改变状态把这个限制变得好搞些
对于每个环,建一个点,设\(i\)对应的环为\(CP_i\)和\(CQ_i\)。然后记\(S\)表示\(A_i=i\)或\(B_i=Q_i\)时的点集,\(T\)表示\(A_i=P_i\)或\(B_i=i\)时的点集
于是上述五种情况,分别对应下述连接方式:
- 不连边,直接计算
- 源向\(CQ_i\)连一条流量为1的边(在\(T\)集花费1代价)
- 汇向\(CP_i\)连一条流量为1的边(在\(S\)集花费1代价)
- \(CP_i\)向\(CQ_i\)连一条流量为1的边(\(A_i\)在\(S\),\(B_i\)在\(T\)花费1代价)
- \(CP_i\)和\(CQ_i\)连流量为1的双向边 (\(A_i\)和\(B_i\)在不同点集花费1代价)
最小割跑一下就行了
代码
#include <bits/stdc++.h>
#define N 100005
#define ll long long
#define For(i,x,y) for(int i=(x);i<=(y);++i)
#define Rof(i,x,y) for(int i=(x);i>=(y);--i)
#define Edge(x) for(int i=head[x];i;i=e[i].nxt)
#define Cur(x) for(int &i=cur[x];i;i=e[i].nxt)
#define mset(x,y) memset(x,y,sizeof(x))
using namespace std;
const int S=0,T=200015;
int dep[N*2],cur[N*2],head[N*2],cnt=1,tot=0,p[N],q[N],_p[N],_q[N];
queue<int> Q;
struct ed{ int v,nxt,f; }e[N<<3];
void add(int u,int v,int f){
e[++cnt]=(ed){v,head[u],f},head[u]=cnt;
e[++cnt]=(ed){u,head[v],0},head[v]=cnt;
}
void dfs1(int x,int y){ if(_p[x])return;_p[x]=tot;dfs1(p[x],y); }
void dfs2(int x,int y){ if(_q[x])return;_q[x]=tot;dfs2(q[x],y); }
bool bfs(){
For(i,0,T) dep[i]=0;
dep[S]=1;Q.push(S);
while(!Q.empty()){
int x=Q.front();Q.pop();
Edge(x){
int to=e[i].v;
if(!dep[to] && e[i].f){
dep[to]=dep[x]+1;
Q.push(to);
}
}
}
return dep[T];
}
int dfs(int x,int f){
if(x==T) return f;
int use=0,o,w;
Cur(x){
int to=e[i].v;
if(e[i].f && dep[to]==dep[x]+1){
o=min(f-use,e[i].f);
w=dfs(to,o);
use+=w;
e[i].f-=w,e[i^1].f+=w;
if(use==f) return f;
}
}
return use;
}
int main(){
int n,ans;
scanf("%d",&n);tot=0,ans=n;
For(i,1,n) scanf("%d",&p[i]),p[i]++;
For(i,1,n) scanf("%d",&q[i]),q[i]++;
For(i,1,n) if(!_p[i]) ++tot,dfs1(i,i);
For(i,1,n) if(!_q[i]) ++tot,dfs2(i,i);
For(i,1,n){
if(p[i]==i && q[i]==i) ans--;
else{
if(p[i]==i && q[i]!=i) add(S,_q[i],1);
else if(p[i]!=i && q[i]==i) add(_p[i],T,1);
else{
add(_p[i],_q[i],1);
if(p[i]==q[i]) add(_q[i],_p[i],1);
}
}
}
while(bfs()){
For(i,0,T) cur[i]=head[i];
ans-=dfs(S,2001010);
}
printf("%d\n",ans);
}
标签:CP,int,最小,rightarrow,CQ,define,AGC038F,neq 来源: https://www.cnblogs.com/PsychicBoom/p/11793999.html