7394. 【2021.11.17NOIP提高組聯考】排
阿新 • • 發佈:2021-11-17
Description
給定兩個 \(0-(N-1)\) 的排列 \(\left\{P_{i}\right\}\) 和 \(\left\{Q_{i}\right\}\) 。要求構造兩個 \(0-(N-1)\) 的排列 \(\left\{A_{i}\right\}\) 和 \(\left\{B_{i}\right\}\), 且滿足對 \(\forall 0 \leq i<N, A_{i}=P_{i}\) 或 \(i, B_{i}=Q_{i}\) 或 \(i\) 。
要求最大化 \(A_{i} \neq B_{i}\) 的數量, 輸出這個最大值。
\(1\le n\le 10^5\)。
Solution
宣告:拆環指的是當前位置選擇 \(i\)。
考慮將 \(P\) 中的迴圈看做一個整體,因為發現,如果對於環上任意一個位置,將其拆掉,可以推導得出整個環都會被拆。\(Q\) 同理。
同時根據正難則反的思想,將題目轉換成出至少有多少個位置 \(A_i=B_i\)。
分情況討論一下什麼情況下會產生貢獻:
- 當 \(P_i=Q_i=i\) 時,這種情況下無論什麼情況 \(A_i=B_i\),答案 -1 即可。
- 當 \(P_i=i,Q_i\not=i\) 時,將 \(Q_i\) 對應的環拆掉會產生貢獻,否則不會。
- 當 \(P_i\not=i,Q_i=i\) 時,同上,將 \(P_i\) 對應的環拆掉才會產生貢獻,否則不會。
- 當 \(P_i\not=i,Q_i\not=i,P_i\not=Q_i\) 時,同時拆掉兩個環才會產生貢獻,否則不會。
- 當 \(P_i\not=i,Q_i\not=i,P_i=Q_i\) 時,同時拆或者同時不拆都會產生貢獻。
想到這裡,網路流就呼之欲出(或許吧)。
將 \(P_i\) 對應的環放在左邊, \(Q_i\) 對應的環放在右邊,設源點 \(S\) 和匯點 \(T\)。
根據上述情況連邊,連邊如下(一一對應):
- 不用連邊,答案直接減。
- \(Q_i\) 對應的環向 \(T\) 連一條流量為 1 的邊。
- \(S\) 向 \(P_i\) 對應的環連一條流量為 1 的邊。
- \(Q_i\)
- \(P_i\) 對應的環向 \(Q_i\) 對應的環連流量為 1 的邊,\(Q_i\) 對應的環也向 \(P_i\) 對應的環連流量為 1 的邊。(兩條)
然後愉快的套上 \(\mathcal{Dinic}\) 就好了。
Code
#include<queue>
#include<cstdio>
#define N 100005
#define inf 2147483647
using namespace std;
struct node
{
int to,head,next,flow;
}a[N<<2];
int n,S,T,tot=1,cnt,ans,p[N],q[N],r1[N],r2[N],cur[N],deep[N];
bool bj1[N],bj2[N];
void getring1(int x)
{
++cnt;
while (!bj1[x]) r1[x]=cnt,bj1[x]=true,x=p[x];
}
void getring2(int x)
{
++cnt;
while (!bj2[x]) r2[x]=cnt,bj2[x]=true,x=q[x];
}
void add(int x,int y,int z)
{
a[++tot].to=y;a[tot].flow=z;a[tot].next=a[x].head;a[x].head=tot;
a[++tot].to=x;a[tot].flow=0;a[tot].next=a[y].head;a[y].head=tot;
}
bool bfs()
{
queue<int> q;
for (int i=1;i<=cnt;++i)
deep[i]=0;
q.push(S);deep[S]=1;
while (!q.empty())
{
int x=q.front();q.pop();
for (int i=a[x].head;i;i=a[i].next)
{
int v=a[i].to;
if (!deep[v]&&a[i].flow) deep[v]=deep[x]+1,q.push(v);
}
}
return deep[T]!=0;
}
int dfs(int x,int fl)
{
if (x==T) return fl;
int res=fl;
for (int i=cur[x];i;i=a[i].next)
{
int v=a[i].to;
cur[x]=i;
if (deep[v]==deep[x]+1&&a[i].flow)
{
int f=dfs(v,min(res,a[i].flow));
a[i].flow-=f;a[i^1].flow+=f;res-=f;
if (!res) break;
}
}
return fl-res;
}
int dinic()
{
int res=0;
while (bfs())
{
for (int i=1;i<=cnt;++i)
cur[i]=a[i].head;
res+=dfs(S,inf);
}
return res;
}
int main()
{
freopen("per.in","r",stdin);
freopen("per.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d",&p[i]),++p[i];
for (int i=1;i<=n;++i)
scanf("%d",&q[i]),++q[i];
for (int i=1;i<=n;++i)
if (!bj1[i])
getring1(i);
for (int i=1;i<=n;++i)
if (!bj2[i])
getring2(i);
S=++cnt;T=++cnt;
ans=n;
for (int i=1;i<=n;++i)
{
if (p[i]==q[i]&&p[i]==i) ans--;
else if (q[i]==i) add(S,r1[i],1);
else if (p[i]==i) add(r2[i],T,1);
else if (p[i]==q[i]) add(r2[i],r1[i],1),add(r1[i],r2[i],1);
else add(r2[i],r1[i],1);
}
printf("%d\n",ans-dinic());
return 0;
}