• AT5203 [AGC038F] Two Permutations 题解


    ATcoder
    Luogu

    Description.

    给定两个长度为 \(n\) 的排列 \(P,Q\),你需要构造两个排列 \(A,B\),满足

    • \(\forall i\in[1,n],A_i=P_i\lor A_i=i\)
    • \(\forall i\in[1,n],B_i=Q_i\lor B_i=i\)

    最大化 \(\sum_{i=1}^n[A_i\ne B_i]\),输出最大值。

    Solution.

    刚开始想错了一步,导致卡住了。
    否则大概能想到网络流建模需求那里。
    刚开始觉得某个排列的环是可以只拆出一个元素的,但是如果这样至少有一个不满足 \(P_i=A_i\)

    就你对于每个排列,拆环
    每个环要么被拆成自环要么不变。
    分讨

    1. \(A_i=i,B_i=i\):恒为 \(0\)
    2. \(A_i\ne i,B_i=i\)\(pid_i\) 肢解使答案减少 \(1\)
    3. \(A_i=i,B_i\ne i\)\(qid_i\) 肢解使答案减少 \(1\)
    4. \(A_i\ne i,B_i\ne i,a_i\ne b_i\):都肢解使答案减少 \(1\)
    5. \(A_i\ne i,B_i\ne i,a_i\ne b_i\):都肢解或都不肢解使答案减少 \(1\)

    就这种限制类型为要么一边要么另一边的基本就想到两个东西:2-sat 和 最小割。
    2-sat 是判断可行性的问题,没法最优化,所以这题应该是最小割。

    考虑限制类型:相同答案减一,看上去很难做。
    考虑有无特殊限制:每次相同答案减一限制连边,会构成二分图。
    这启发我们翻转一部的状态,定义一个割集中某个环 \(\in S\) 当且仅当

    • 若它是 \(p\) 的环,且它被肢解了
    • 若它是 \(q\) 的环,且它没被肢解

    这样就可以做了,以上连边分别是

    1. 答案减 \(1\)
    2. \(pid_i\rightarrow T\)(它被分在 \(S\) 集合答案少 \(1\)
    3. \(S\rightarrow qid_i\)(它被分在 \(T\) 集合答案少 \(1\)
    4. \(pid_i\rightarrow qid_i\)\(pid_i\) 被分在 \(S\)\(qid_i\) 被分在 \(T\) 答案少 \(1\)
    5. \(pid_i\rightarrow qid_i,qid_i\rightarrow pid_i\)\(pid_i,qid_i\) 集合不同答案少 \(1\)

    然后直接最小割就行了。

    Coding.

    点击查看代码
    //是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了{{{
    #include<bits/stdc++.h>
    using namespace std;typedef long long ll;
    template<typename T>inline void read(T &x)
    {
    	x=0;char c=getchar(),f=0;
    	for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
    	for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    	f?x=-x:x;
    }
    template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}/*}}}*/
    namespace Flow//{{{
    {
    	struct edge{int to,w,nxt;}e[10000005];int et=1,head[200005],d[200005],cur[200005];
    	inline void ADDE(int x,int y,int w) {e[++et]=(edge){y,w,head[x]},head[x]=et;}
    	inline void adde(int x,int y,int w) {ADDE(x,y,w),ADDE(y,x,0);}
    	inline char bfs(int s,int t)
    	{
    		int hd=1,tl=1;cur[1]=s,memset(d,0,sizeof(d)),d[s]=1;
    		for(int x=s;hd<=tl;x=cur[++hd]) for(int i=head[x];i;i=e[i].nxt)
    			if(!d[e[i].to]&&e[i].w) {d[cur[++tl]=e[i].to]=d[x]+1;if(e[i].to==t) return 1;}
    		return 0;
    	}
    	inline int dfs(int x,int t,int lim=1e9)
    	{
    		int f=lim;if(x==t) return lim;
    		for(int &i=cur[x];i;i=e[i].nxt) if(d[e[i].to]==d[x]+1&&e[i].w)
    			{int g=dfs(e[i].to,t,min(f,e[i].w));e[i].w-=g,e[i^1].w+=g,f-=g;if(!f) break;}
    		return lim-f;
    	}
    	inline int dinic(int s,int t) {int r=0;while(bfs(s,t)) memcpy(cur,head,sizeof(cur)),r+=dfs(s,t);return r;}
    }//}}}
    int n,p[100005],q[100005],ip[100005],iq[100005],idt=0;
    int main()
    {
    	read(n);for(int i=1;i<=n;i++) read(p[i]),p[i]++;
    	int r=n;for(int i=1;i<=n;i++) read(q[i]),q[i]++;
    	for(int i=1;i<=n;i++) if(!ip[i]&&p[i]!=i) {ip[i]=++idt;for(int x=i;p[x]!=i;x=p[x]) ip[p[x]]=idt;}
    	for(int i=1;i<=n;i++) if(!iq[i]&&q[i]!=i) {iq[i]=++idt;for(int x=i;q[x]!=i;x=q[x]) iq[q[x]]=idt;}
    	int s=idt+1,t=s+1;//分在 s:p 肢解或 q 不肢解
    	for(int i=1;i<=idt;i++) Flow::adde(s,i,1),Flow::adde(i,t,1);
    	for(int i=1;i<=n;i++)
    		if(p[i]==i&&q[i]==i) r--;
    		else if(p[i]==i) Flow::adde(s,iq[i],1);//如果 q 肢解(T)答案-1
    		else if(q[i]==i) Flow::adde(ip[i],t,1);//如果 p 肢解(S)答案-1
    		else if(p[i]!=q[i]) Flow::adde(ip[i],iq[i],1);//都肢解 (1S 1T) 答案-1
    		else Flow::adde(ip[i],iq[i],1),Flow::adde(iq[i],ip[i],1);//都肢解答案-1,都不肢解答案-1
    	return printf("%d\n",r-Flow::dinic(s,t)+idt),0;
    }
    
  • 相关阅读:
    Linux systemtap定位系统IO资源使用情况(ok)
    DISK 100% BUSY,谁造成的?(ok)
    容易被误读的IOSTAT
    利用BLKTRACE分析IO性能
    iowait 过高问题的查找及解决linux
    top后台执行显示:top: failed tty get 错误
    Nginx解读内置非默认模块 ngx_http_stub_status_module
    Nginx健康检查模块
    平滑升级你的Nginx
    Practice telephone techniques
  • 原文地址:https://www.cnblogs.com/pealfrog/p/15555556.html
Copyright © 2020-2023  润新知