给定一个排列,每次将排列最后一位移至第一位,然后将其他的顺次延后。
问你进行几次这个操作后
∑ni=1|a[i]−i|
最小
输出这个值与进行的操作数。
考试的时候就想着瞎搞,毕竟n太大了,O(nlogn)都会炸的感觉。
首先可以将a[i]扩长一倍,这样子就可以简单的每次检视数列。
首先考虑每一次操作对答案的影响,对于每次操作相当于每个值有i++,维护两种值的个数,a[i]
<
<script type="math/tex" id="MathJax-Element-489"><</script>i(low)和a[i]
>
i(high)。
那么对于a[i]
那么每次操作之后答案就会变为ans-high+low,然后对于排列的最后一位,它的贡献直接暴力处理即可,那么加上之后,每次操作后会有
ans=ans−high+low−n−2+2∗a[2∗n−i]
#include<bits/stdc++.h>
#define fer(i,j,n) for(int i=j;i<=n;i++)
#define far(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
const int maxn=2e6+10;
const int INF=1e9+7;
using namespace std;
/*----------------------------------------------------------------------------*/
inline ll read()
{
char ls;ll x=0,sng=1;
for(;ls<'0'||ls>'9';ls=getchar())if(ls=='-')sng=-1;
for(;ls>='0'&&ls<='9';ls=getchar())x=x*10+ls-'0';
return x*sng;
}
/*----------------------------------------------------------------------------*/
int a[maxn],b[maxn];
ll n,ans,sum,pt,high,low;
int main()
{
n=read();
fer(i,1,n)
{
a[i]=read();
a[i+n]=a[i];
sum+=abs(a[i]-i);
if(a[i]>=i)b[a[i]-i]++,high++;
else low++;
}
ans=sum;
fer(i,0,n-2)
{
high-=b[i];low+=b[i];
sum=sum+low-high-n-2+2*a[2*n-i];
b[a[2*n-i]+i]++;
high++;low--;
if(sum<ans)
ans=sum,pt=i+1;
//cout<<i+1<<" "<<ans<<endl;
}
cout<<ans<<" "<<pt<<endl;
}