题解:二分答案。
这道题貌似是某年IOI的弱化版。
有一个非常厉害也非常好用的结论:对于游戏的一个形如“ABABABABA.."的操作序列,在所有A操作不变得前提下,可以找到一个形如”AAAAABBBBB"的操作序列,使两个序列作用于同一个排列的效果相同。
有了这个结论的基础这个题就好做多了。问题就转化成了最少需要几次操作将序列变成升序(可以交换不相邻的,如果只能交换相邻的就变成了树状数组求逆序对)
这个需要怎么搞呢?我们可以从前向后扫,如果当前位置的数不是应该在这个位置的数,就找到应该在这个位置的数,然后将其交换,最终交换了几次最少的操作次数就是几。
但是还有别的方法:
以5 4 3 2 1 为例
我们可以发现5,1虽然不在自己应该在的位置,但是如果把它们两个看成整体,对于整个序列来说它们占据了排好序后5,1应该在的位置,所以对于整个序列来说是有序的,它们只是自身内部无序而已。5应该到1处,1应该到5处,形成了一个循环,所以可以将它们抽象成一个环,环内换序就可以了。(下面把这种环称为循环节)
对于一个含有n个元素的循环节来说,要使其有序,要交换n-1次(前面都排好了,最后一个数自然有序就不用排了)。
上例中3在原本就在的位置,可以看成一个元素的循环节。
我们可以推断出有一个循环节,就可以少交换一次,因为n个元素的循环节,只需交换n-1次即可有序。
那么对于整个序列来说,最少交换次数为 元素总数-循环节个数。那么例子中的答案就是2.
然后这道题其实就只需要二分答案然后判定了。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 600003
using namespace std;
int x[N],y[N],a[N],b[N],pos[N],n,m;
bool pd(int num)
{
for (int i=1;i<=n;i++) b[i]=a[i];
for (int i=1;i<=num;i++) swap(b[x[i]],b[y[i]]);
for (int i=1;i<=n;i++) pos[b[i]]=i;
int t=0;
for (int i=1;i<=n;i++)
if (b[i]!=i) {
int now=b[i]; int p=pos[i];
swap(b[i],b[pos[i]]),t++;
pos[now]=p;
}
if (t<=num) return true;
return false;
}
int main()
{
freopen("sort.in","r",stdin);
freopen("sort.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]++;
scanf("%d",&m);
for (int i=1;i<=m;i++) scanf("%d%d",&x[i],&y[i]),x[i]++,y[i]++;
int l=0; int r=m; int ans=r;
while(l<=r) {
int mid=(l+r)/2;
if (pd(mid)) ans=min(ans,mid),r=mid-1;
else l=mid+1;
}
printf("%d\n",ans);
}
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=610000;
int n,m,i,a[N],s[N],x[N],y[N],vis[N];
int check()
{
int ret=0;
memset(vis,0,sizeof(vis));
for (int i=0;i<n;i++)
{
if (vis[i]) continue;
int x=a[i],j=a[i];
while (a[j]!=x) {ret++;vis[j]=1;j=a[j];}
}
return ret;
}
int main()
{
freopen("sort.in","r",stdin);
freopen("sort.out","w",stdout);
scanf("%d",&n);
for (i=0;i<n;i++) scanf("%d",&s[i]);
scanf("%d",&m);
for (i=1;i<=m;i++) scanf("%d%d",&x[i],&y[i]);
int l=0,r=m;
while (l<r)
{
int mid=(l+r)>>1;
for (i=0;i<n;i++) a[i]=s[i];
for (i=1;i<=mid;i++) swap(a[x[i]],a[y[i]]);
if (check()<=mid) r=mid;
else l=mid+1;
}
cout<<l<<endl;
return 0;
}