1. 程式人生 > 其它 >7394. 【2021.11.17NOIP提高組聯考】排

7394. 【2021.11.17NOIP提高組聯考】排

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\)

分情況討論一下什麼情況下會產生貢獻:

  1. \(P_i=Q_i=i\) 時,這種情況下無論什麼情況 \(A_i=B_i\),答案 -1 即可。
  2. \(P_i=i,Q_i\not=i\) 時,將 \(Q_i\) 對應的環拆掉會產生貢獻,否則不會。
  3. \(P_i\not=i,Q_i=i\) 時,同上,將 \(P_i\) 對應的環拆掉才會產生貢獻,否則不會。
  4. \(P_i\not=i,Q_i\not=i,P_i\not=Q_i\) 時,同時拆掉兩個環才會產生貢獻,否則不會。
  5. \(P_i\not=i,Q_i\not=i,P_i=Q_i\) 時,同時拆或者同時不拆都會產生貢獻。

想到這裡,網路流就呼之欲出(或許吧)。

\(P_i\) 對應的環放在左邊, \(Q_i\) 對應的環放在右邊,設源點 \(S\) 和匯點 \(T\)

根據上述情況連邊,連邊如下(一一對應):

  1. 不用連邊,答案直接減。
  2. \(Q_i\) 對應的環向 \(T\) 連一條流量為 1 的邊。
  3. \(S\)\(P_i\) 對應的環連一條流量為 1 的邊。
  4. \(Q_i\)
    對應的環向 \(P_i\) 對應的環連流量為 1 的邊。(避免出現產生一些不合法的答案)
  5. \(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;	
}