题解:AT_abc436_e [ABC436E] Minimum Swap
Skyler_yunxi · · 题解
赛时卡了
我们将
每次操作相当于使
考虑在这种情况下每次操作会带来什么贡献。
- 若操作的
P_i,P_j 在同一环内,则我们发现这次操作会把这个环拆成两个环,造成了1 的贡献。 - 若操作的
P_i,P_j 不在同一环内,则本次操作会合并两个环,造成-1 的贡献。
我们显然希望环的数量最多,因此只有 1 类操作是有价值的,因此我们相当于统计不同的 1 类操作的个数。
那么就很好计数了,任意环内的任意点对均为合法操作,因此我们设
简单用并查集维护连通块即可。
::::success[Code]
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned ll
const int N=3e5+5;
int p[N],n,cnt,sum,id[N],f[N],sz[N];
ll ans;
int gf(int x){return f[x]==x?x:f[x]=gf(f[x]);}
void hb(int x,int y){
if(gf(x)==gf(y))return;
sz[gf(y)]+=sz[gf(x)];
f[gf(x)]=gf(y);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&p[i]),id[p[i]]=i,sz[i]=1,f[i]=i;
for(int i=1;i<=n;++i){
if(p[i]==i)continue;
hb(p[i],i);
}
for(int i=1;i<=n;++i)
if(f[i]==i)ans+=1ll*sz[i]*(sz[i]-1)/2;
printf("%lld",ans);
return 0;
}
::::