題解:AT_agc038_f [AGC038F] Two Permutations
題目:
置換環是顯然的,一個環有旋一下和不旋兩種狀態。
\((P_i=i,Q_i=i,P_i=Q_i)\) 無非這三個限制。
- \((0,0,0)\):旋一個以上就有貢獻。
- \((0,0,1)\):旋一個才有貢獻。
- \((0,1,0)\):旋 P 才有貢獻。
- \((1,0,0)\):旋 Q 才有貢獻。
- \((1,1,1)\):沒貢獻。
- \((0,1,1)\):不合法。
- \((1,0,1)\):不合法。
- \((1,1,0)\):不合法。
最小割!直接出來了!
設 S、T 點后肯定要再把每個置換環當點塞進去,思考一個點 \(i\) 怎么連邊:
下面稱 \(p_i\) 為 \(P_i\) 在的置換環,\(q_i\) 為 \(Q_i\) 在的置換環。
假定連 \(S\) 為旋,連 \(T\) 為沒旋。
第一種:wc。
trick:假定 \(p_i\) 連 \(S\) 為旋,\(q_i\) 連 \(T\) 為旋。
第一種:\(S→q_i→p_i→T\) 無法貢獻,連 \(q_i→p_i\) 邊權為 \(1\)。
第二種:\(S→q_i→p_i→T,S→p_i→q_i→T\) 無法貢獻,連 \(q_i→p_i,p_i→q_i\) 邊權為 \(1\)。
第三種:\(S→q_i→…\) 無法貢獻,連 \(S→q_i\) 邊權為 \(1\)。
第四種:\(p_i→T→…\) 無法貢獻,連 \(p_i→T\) 邊權為 \(1\)。
第五種:直接 ans--。
初始令 \(ans=n\),減去第五種情況直接跑就行。
#include<bits/stdc++.h>
using namespace std;
const int QAQ=2e6+9,inf=1e9;
int dis[QAQ],kai[QAQ],lim;
int n,p[QAQ],q[QAQ],cnt=1,head[QAQ],nex[QAQ*5],to[QAQ*5],w[QAQ],ans;
void lian2(int u,int v,int wa)
{
to[++cnt]=v;
nex[cnt]=head[u];
w[cnt]=wa;
head[u]=cnt;
}
void lian(int u,int v,int w)
{
lian2(u,v,w);
lian2(v,u,0);
}
int s,t;
queue<int> dui;
int bfs()
{
for(int i=1;i<=lim;i++) dis[i]=inf;
dis[s]=0;
kai[s]=head[s];
while(!dui.empty()) dui.pop();
dui.push(s);
while(!dui.empty())
{
int x=dui.front();dui.pop();
for(int i=head[x];i;i=nex[i])
{
int v=to[i];
if(w[i]>0&&dis[v]==inf)
{
kai[v]=head[v];
dis[v]=dis[x]+1;
dui.push(v);
}
}
}
if(dis[t]!=inf) return 1;
return 0;
}
int dfs(int x,int xian)
{
if(x==t) return xian;
int ans=0;
for(int i=kai[x];i&&xian>0;i=nex[i])
{
kai[x]=i;
int v=to[i];
if(dis[v]==dis[x]+1&&w[i]>0)
{
int k=dfs(v,min(xian,w[i]));
w[i]-=k,w[i^1]+=k;
ans+=k,xian-=k;
if(k==0) dis[v]=inf;
}
}
return ans;
}
int cnt1,cnt2;
struct xxx
{
int f[QAQ];
int find(int x) {return x==f[x]?f[x]:f[x]=find(f[x]);}
void hebing(int x,int y)
{
x=find(x),y=find(y);
if(x==y) return ;
f[x]=y;
}
void cs()
{
for(int i=0;i<n;i++) f[i]=i;
}
} ta,tb;
int duia[QAQ],duib[QAQ];
signed main()
{
cin>>n;
ta.cs(),tb.cs();
for(int i=0;i<n;i++) cin>>p[i],ta.hebing(i,p[i]);
for(int i=0;i<n;i++) cin>>q[i],tb.hebing(i,q[i]);
for(int i=0;i<n;i++)
{
if(i==ta.find(i)) duia[i]=++cnt1;
if(i==tb.find(i)) duib[i]=++cnt2;
}
for(int i=0;i<n;i++) duia[i]=duia[ta.find(i)],duib[i]=duib[tb.find(i)];
lim=cnt1+cnt2;
for(int i=0;i<n;i++) duib[i]+=cnt1;
s=lim+1,t=lim+2;
lim+=2;
for(int i=1;i<=cnt1;i++) lian(s,i,0),lian(i,t,0);
for(int i=1;i<=cnt2;i++) lian(s,cnt1+i,0),lian(cnt1+i,t,0);
ans=n;
for(int i=0;i<n;i++)
{
if(p[i]!=i&&q[i]!=i&&p[i]!=q[i]) lian(duib[i],duia[i],1);
else if(p[i]!=i&&q[i]!=i&&p[i]==q[i]) lian(duia[i],duib[i],1),lian(duib[i],duia[i],1);
else if(p[i]==i&&q[i]!=i&&p[i]!=q[i]) lian(duib[i],t,1);
else if(p[i]!=i&&q[i]==i&&p[i]!=q[i]) lian(s,duia[i],1);
else if(p[i]==i&&q[i]==i&&p[i]==q[i]) ans--;
}
while(bfs()) ans-=dfs(s,inf);
cout<<ans;
return 0;
}

浙公網安備 33010602011771號