Atcoder Grand Contest 038 F - Two Permutations(集合划分模型+最小割)
好久前做的题了……今天偶然想起来要补个题解
首先考虑排列 \(A_i\) 要么等于 \(i\),要么等于 \(P_i\) 这个条件有什么用。我们考虑将排列 \(P_i\) 拆成一个个置换环,那么对于每一个 \(i\),根据其置换环的情况可以分出以下几类:
- 如果 \(i\) 所在置换环大小为 \(1\),即 \(P_i=i\),那么 \(A_i\) 别无选择,只能等于 \(i\)
- 如果 \(i\) 所在置换环大小不为 \(1\),那么 \(A_i\) 有两种选择,\(A_i=i\) 或者 \(A_i=P_i\)
- 如果 \(A_i=i\),那么假设 \(j\) 为满足 \(P_j=i\) 的位置,那么由于排列中元素不能重复,因此 \(A_j\ne P_j=i\),即 \(A_j=j\),我们再找出 \(P_k=j\) 的 \(k\),也应有 \(A_k=k\),这样即可确定整个置换环上元素的情况。
- 如果 \(A_i=P_i\),类似地,设 \(j=P_i\),那么 \(A_j\ne j\),因为排列中元素不能重复,故 \(A_j=P_j\),我们再找出 \(k=P_j\) 的位置 \(k\),也应有 \(A_k=P_k\),这样也能够确定整个置换环的 \(A\)。
也就是说,对于一个置换环而言,我们可以将其视作一个整体看待——这个置换环中要么所有元素的 \(A_i\) 都等于其本身,要么所有元素的 \(A_i\) 都等于 \(P_i\),为了使表述更加具体形象,我们把前一种情况称作“转”(orz wlzhouzhuan),后一种情况称作“不转”。那么对于每一个下标 \(i\),它是否产生的 \(A_i=B_i\) 的情况如下:
- 如果 \(i=P_i=Q_i\),那么不管怎样都有 \(A_i=B_i\),我们完全可以直接令答案加一,并忽略这种情况。
- 如果 \(i=P_i\ne Q_i\),那么若 \(Q_i\) 所在置换环不转就会有 \(A_i=B_i=i\),对答案产生 \(1\) 的贡献,若 \(Q_i\) 所在置换环转则不会产生这样的情况。
- 如果 \(i=Q_i\ne P_i\),同理,若 \(P_i\) 所在置换环不转则重复元素个数 \(+1\),否则重复元素个数不变。
- 如果 \(i\ne P_i=Q_i\),那么如果 \(P_i\) 所在置换环与 \(Q_i\) 所在置换环同时转/同时不转那么重复元素个数 \(+1\),否则重复元素个数不变。
- 如果 \(i\ne P_i\ne Q_i\),那么如果 \(P_i\) 所在置换环与 \(Q_i\) 所在置换环同时不转那么重复元素个数 \(+1\),否则重复元素个数不变。
如果我们将每个置换环“转”看作被划分入 A 集合,“不转”看作被划分入 B 集合,那么上述条件可以转化为:
- \(i=P_i\ne Q_i\):如果 \(Q_i\) 所在置换环属于 B 那么答案加 \(1\)
- \(i=Q_i\ne P_i\):如果 \(P_i\) 所在置换环属于 B 那么答案加 \(1\)
- \(i\ne P_i=Q_i\):如果 \(P_i,Q_i\) 所在置换环属于相同集合那么答案加 \(1\)
- \(i\ne P_i\ne Q_i\):如果 \(P_i,Q_i\) 都属于 B 集合那么答案加 \(1\)
看到“划分为两个集合”,“如果两点属于相同/不同集合则代价加 \(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 Grand Contest 038 F - Two Permutations(集合划分模型+最小割)的更多相关文章
- AtCoder Grand Contest 038题解
好久没更了 写点东西吧= = A 01Matrix 简单构造 左上角和右下角染成1其他染成0即可 #include<bits/stdc++.h> #define ll long long ...
- AtCoder Grand Contest 038 简要题解
从这里开始 比赛目录 Problem A 01 Matrix Code #include <bits/stdc++.h> using namespace std; typedef bool ...
- AtCoder Grand Contest 038 题解
传送门 这场表现的宛如一个\(zz\) \(A\) 先直接把前\(b\)行全写成\(1\),再把前\(a\)列取反就行 const int N=1005; char mp[N][N];int n,m, ...
- AtCoder Grand Contest 016 F - Games on DAG
题目传送门:https://agc016.contest.atcoder.jp/tasks/agc016_f 题目大意: 给定一个\(N\)点\(M\)边的DAG,\(x_i\)有边连向\(y_i\) ...
- AtCoder Grand Contest 002 F:Leftmost Ball
题目传送门:https://agc002.contest.atcoder.jp/tasks/agc002_f 题目翻译 你有\(n*k\)个球,这些球一共有\(n\)种颜色,每种颜色有\(k\)个,然 ...
- AtCoder Grand Contest 017 F - Zigzag
题目传送门:https://agc017.contest.atcoder.jp/tasks/agc017_f 题目大意: 找出\(m\)个长度为\(n\)的二进制数,定义两个二进制数的大小关系如下:若 ...
- AtCoder Grand Contest 003 F - Fraction of Fractal
题目传送门:https://agc003.contest.atcoder.jp/tasks/agc003_f 题目大意: 给定一个\(H×W\)的黑白网格,保证黑格四连通且至少有一个黑格 定义分形如下 ...
- AtCoder Grand Contest 011 F - Train Service Planning
题目传送门:https://agc011.contest.atcoder.jp/tasks/agc011_f 题目大意: 现有一条铁路,铁路分为\(1\sim n\)个区间和\(0\sim n\)个站 ...
- AtCoder Grand Contest 010 F - Tree Game
题目传送门:https://agc010.contest.atcoder.jp/tasks/agc010_f 题目大意: 给定一棵树,每个节点上有\(a_i\)个石子,某个节点上有一个棋子,两人轮流操 ...
随机推荐
- SingnalR 从开发到生产部署闭坑指南
前天倒腾了一份[SignalR在react/go技术栈的实践], 步骤和思路大部分是外围框架的应用, 今天趁热打铁, 给一个我总结的SignalR避坑指南. 1.SignalR 默认协商 不管是.NE ...
- javascript-jquery对象的css处理
一.css基本属性处理 1.css()方法:获取css属性值.$("选择器").css(name);//获取匹配选择器的元素指定css属性值. 2.css()方法:设置css属性值 ...
- [Git系列] 前言
Git 简介 Git 是一个重视速度的分布式版本控制和代码管理系统,最初是由 Linus Torvalds 为开发 Linux 内核而设计并开发的,是一款遵循二代 GUN 协议的免费软件.这一教程会向 ...
- SpringCloud 2020.0.4 系列之 Feign
1. 概述 老话说的好:任何问题都有不止一种的解决方法,当前的问题没有解决,只是还没有发现解决方法,而并不是无解. 言归正传,之前我们聊了 SpringCloud 的服务治理组件 Eureka,今天我 ...
- Ubuntu16.04安装apache hive
0.常规配置操作可参照以下网址: 0.1 Ubuntu安装hive,并配置mysql作为元数据库http://dblab.xmu.edu.cn/blog/install-hive/ ------以下为 ...
- Open vSwitch 应用实践
基础配置以及要点: 1.交换机创建和端口的配置 1) 创建一个新的 OVS 交换机[格式:$ ovs-vsctl add-br [名称]] $ovs-vsctl add-br ovs-switch 2 ...
- Eclipse简单介绍
1.编码设置:Windows>preference>Workspace>Other-UTF-8>apply and close: 2.字体大小设置:Windows>pre ...
- RF运行之后控制信息日志显示乱码(解决方法)
RIDE运行自动化测试案例时,控制台信息显示乱码,如下所示: 解决方法:进入到python的安装路径下:D:\python37\Lib\site-packages\robotide\contrib\t ...
- Emmet语法 —— 快速生成HTML结构
快速生成HTML结构语法 1.生成单个标签 : 标签名+tab,比如 div 然后tab 键, 就可以生成 <div></div> 2.生成多个相同标签 div*3 + tab ...
- Spark中的两种模式
两种模式 client-了解 cluster模式-开发使用 操作 1.需要Yarn集群 2.历史服务器 3.提交任务的的客户端工具-spark-submit命令 4.待提交的spark任务/程序的字节 ...