最小割AGC038F

Posted psychicboom

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最小割AGC038F相关的知识,希望对你有一定的参考价值。

题目大意:

给你两个长为(n)的排列(P),(Q),构造两个长为(n)的排列(A),(B),满足对于(1leq ileq n)(A_i=i;or;P_i),(B_i=i;or;Q_i)

在此基础上求(A_i eq B_i)的元素的最大个数

(nleq 1e5)

这是一个神仙网络流QAQ

首先这个问题可以转化成在满足题目限制的情况下,求最少的(A_i=B_i)(i)的个数

你把(i ightarrow p_i)(i ightarrow q_i)连出两张图

然后你发现,在(i ightarrow p_i)上成环的(i)(A_i)选择的状态相同((B_i)同理)

然后分类讨论一下:

  • (P_i=Q_i=i)

    此时不管怎么选(A_i)都等于(B_i)

  • (P_i=i)(Q_i eq i)

    (B_i=i)(A_i)会等于(B_i)

  • (P_i eq i)(Q_i=i)

    (A_i=i)(A_i)会等于(B_i)

  • (P_i eq Q_i eq i)

    (A_i=i)(B_i=i)(A_i)会等于(B_i)

  • (P_i=Q_i eq 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
",ans);
}

以上是关于最小割AGC038F的主要内容,如果未能解决你的问题,请参考以下文章

最小割树

「ZJOI2011」最小割

bzoj4519: [Cqoi2016]不同的最小割(分治最小割)

「CQOI2016」不同的最小割

bzoj2229 Zjoi2011—最小割

关于最小割的进一步理解