其他分享
首页 > 其他分享> > Atcoder Grand Contest 038 F - Two Permutations(集合划分模型+最小割)

Atcoder Grand Contest 038 F - Two Permutations(集合划分模型+最小割)

作者:互联网

洛谷题面传送门 & Atcoder 题面传送门

好久前做的题了……今天偶然想起来要补个题解

首先考虑排列 \(A_i\) 要么等于 \(i\),要么等于 \(P_i\) 这个条件有什么用。我们考虑将排列 \(P_i\) 拆成一个个置换环,那么对于每一个 \(i\),根据其置换环的情况可以分出以下几类:

也就是说,对于一个置换环而言,我们可以将其视作一个整体看待——这个置换环中要么所有元素的 \(A_i\) 都等于其本身,要么所有元素的 \(A_i\) 都等于 \(P_i\),为了使表述更加具体形象,我们把前一种情况称作“转”(orz wlzhouzhuan),后一种情况称作“不转”。那么对于每一个下标 \(i\),它是否产生的 \(A_i=B_i\) 的情况如下:

如果我们将每个置换环“转”看作被划分入 A 集合,“不转”看作被划分入 B 集合,那么上述条件可以转化为:

看到“划分为两个集合”,“如果两点属于相同/不同集合则代价加 \(1\),求最小/最大代价”之类的字眼,我们能够想到……最小割!具体来说,我们将每个置换环看作一个点,并新建源汇,我们定义 \(P\) 中的置换环转当且仅当其与 \(S\) 相连,不转当且仅当其与 \(T\) 相连;\(Q\) 中的置换环转当且仅当其与 \(T\) 相连,不转当且仅当其与 \(S\) 相连,这样所有代价都可以用一/两条网络流上的 \(1\) 权边的形式表述,再根据最大流 \(=\) 最小割求出最小代价即可。

时间复杂度同 dinic 求二分图匹配,\(\mathcal O(n\sqrt{n})\)。

const int MAXN=1e5;
const int MAXV=1e5+2;
const int MAXE=2e5*2;
const int INF=0x3f3f3f3f;
int n,a[MAXN+5],b[MAXN+5],S=1e5+1,T=1e5+2,ncnt=0;
int bel_a[MAXN+5],bel_b[MAXN+5];
int hd[MAXV+5],to[MAXE+5],cap[MAXE+5],nxt[MAXE+5],ec=1;
void adde(int u,int v,int f){
	to[++ec]=v;cap[ec]=f;nxt[ec]=hd[u];hd[u]=ec;
	to[++ec]=u;cap[ec]=0;nxt[ec]=hd[v];hd[v]=ec;
} int dep[MAXV+5],now[MAXV+5];
bool getdep(){
	memset(dep,-1,sizeof(dep));dep[S]=0;
	queue<int> q;q.push(S);now[S]=hd[S];
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int e=hd[x];e;e=nxt[e]){
			int y=to[e],z=cap[e];
			if(z&&!~dep[y]){
				dep[y]=dep[x]+1;
				now[y]=hd[y];q.push(y);
			}
		}
	} return ~dep[T];
}
int getflow(int x,int f){
	if(x==T) return f;int ret=0;
	for(int &e=now[x];e;e=nxt[e]){
		int y=to[e],z=cap[e];
		if(z&&dep[y]==dep[x]+1){
			int w=getflow(y,min(f-ret,z));
			ret+=w;cap[e]-=w;cap[e^1]+=w;
			if(f==ret) return ret;
		}
	} return ret;
}
int dinic(){
	int ret=0;
	while(getdep()) ret+=getflow(S,INF);
	return ret;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),++a[i];
	for(int i=1;i<=n;i++) scanf("%d",&b[i]),++b[i];
	for(int i=1;i<=n;i++) if(!bel_a[i]){
		bel_a[i]=(i^a[i])?(++ncnt):ncnt;int cur=a[i];
		while(cur^i) bel_a[cur]=ncnt,cur=a[cur];
	}
	for(int i=1;i<=n;i++) if(!bel_b[i]){
		bel_b[i]=(i^b[i])?(++ncnt):ncnt;int cur=b[i];
		while(cur^i) bel_b[cur]=ncnt,cur=b[cur];
	} int res=n;
	for(int i=1;i<=n;i++){
		if(a[i]==i&&b[i]==i) res--;
		else if(a[i]!=i&&b[i]!=i){
			if(a[i]==b[i]) adde(bel_a[i],bel_b[i],1),adde(bel_b[i],bel_a[i],1);
			else adde(bel_b[i],bel_a[i],1);
		} else {
			if(a[i]==i) adde(bel_b[i],T,1);
			else adde(S,bel_a[i],1);
		}
	} printf("%d\n",res-dinic());
	return 0;
}

标签:Atcoder,Contest,int,置换,Permutations,ne,ret,dep,ec
来源: https://www.cnblogs.com/ET2006/p/agc038F.html