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\)个石子,某个节点上有一个棋子,两人轮流操 ...
随机推荐
- 巧用 CSS3 filter(滤镜) 属性
原文链接:CSS3 filter(滤镜) 属性 效果预览 filter: grayscale(100%); 定义和使用 filter 属性定义了元素(通常是<img>)的可视效果(例如:模 ...
- 【UE4 C++ 基础知识】<13> 多线程——TaskGraph
概述 TaskGraph 系统是UE4一套抽象的异步任务处理系统 TaskGraph 可以看作一种"基于任务的并行编程"设计思想下的实现 通过TaskGraph ,可以创建任意多线 ...
- 【UE4 设计模式】简单工厂模式 Simple Factory Pattern
概述 描述 又称为静态工厂方法 一般使用静态方法,根据参数的不同创建不同类的实例 套路 创建抽象产品类 : 创建具体产品类,继承抽象产品类: 创建工厂类,通过静态方法根据传入不同参数从而创建不同具体产 ...
- 【数据结构与算法Python版学习笔记】图——骑士周游问题 深度优先搜索
骑士周游问题 概念 在一个国际象棋棋盘上, 一个棋子"马"(骑士) , 按照"马走日"的规则, 从一个格子出发, 要走遍所有棋盘格恰好一次.把一个这样的走棋序列 ...
- Gitflow branch与Docker image tag命名冲突怎么办?
谷歌还是比必应要好用一点. 在前公司,我根据主流的git flow 给团队搭建了一套devops流程,运行在 docker & k8s上. 在现代devops流程中,一般推荐使用git分支名或 ...
- Noip模拟32(再度翻车) 2021.8.7
T1 Smooth 很水的一道题...可是最傻 的是考场上居然没有想到用优先队列优化... 上来开题看到这个,最一开始想,这题能用模拟短除法,再一想太慢了,就想着优化 偏偏想到线性筛然后试别的素 ...
- PCIE学习链接集合
<PCIE基础知识+vivado IP core设置> https://blog.csdn.net/eagle217/article/details/81736822 <一步一步开始 ...
- word-ladder leetcoder C++
Given two words (start and end), and a dictionary, find the length of shortest transformation sequen ...
- Beam Search快速理解及代码解析
目录 Beam Search快速理解及代码解析(上) Beam Search 贪心搜索 Beam Search Beam Search代码解析 准备初始输入 序列扩展 准备输出 总结 Beam Sea ...
- oracle 账号解锁 java.sql.SQLException: ORA-28000: the account is locked
日志报错:ORA-28000: the account is locked 1.plsql登录提示用户被锁定 2.sys登录sqlplus登录查看 SQL> select username,ac ...