給出一個\(n\)元素排列\(p[]\),定義數組\(p[]\)的偏差值爲\(\sum\limits_{i=1}^{i=n} |p[i]-i|\).每次操做都把下標爲\(n\)的數放到下標爲\(1\)的位置,其餘數依次右移,問在經過幾回操做後能使得偏差值最小c++
正解是差分,設\(d[i]\)爲i次操做後的偏差值,考慮\(p[j]\)對\(d[i]\)的貢獻,發現i是一段連續的區間,即區間\([l,r]\)同時加上某個等差數列。git
區間\([l,r]\)加上\(k*(x-l)+b\)。\((l≤x≤r)\) 設置兩個數列\(d[i],f[i]\)。數組
\(f[i]\)的前綴和就是\(i\)位置最終的\(k\)值,再求一次前綴就是\(1\)到\(i\)的\(k\)之和,但因爲咱們只須要\([l+1,x]\)的\(k\)值之和,因此還要將多出的減掉spa
不過咱們還有更簡單的解法code
注意到對於每一個p[i],使得\(p[i]==i\)的時間點只有一個,計算出這個時間點,若p[i]!=1,讓該時間內由p[i]>i變成p[i]==i的數++get
考慮每次右移操做形成的影響,能夠看作\(1\)到\(n-1\)位置的數\(i+1\),\(n\)位置\(i=1\),若能維護出實時的大於\(0\)的個數和小於\(0\)的個數,再單獨考慮最後一個數,就能夠動態的計算答案。因爲使得\(p[i]==i\)的時間點只有一個,故容易預處理每一個時間的變化量。input
//細節太繁瑣,我崩潰了 //果真仍是太菜了 #include<bits/stdc++.h> using namespace std; #define go(i,a,b) for(int i=a;i<=b;++i) #define com(i,a,b) for(int i=a;i>=b;--i) #define mem(a,b) memset(a,b,sizeof(a)) #define fo(i,a) for(int i=0;i<a;++i) #define il inline const int N=1e6+5; typedef long long ll; int p[N],cp=0,cn=0,mink,k,n; ll sum,ans; int idx[N]; il void read(int &x){ x=0;char c=getchar(),f=1; while(!isdigit(c)){ if(c=='-') f=-1; c=getchar(); } while(isdigit(c)){ x=x*10+c-'0'; c=getchar(); } x*=f; } int main(){ //freopen("input.txt","r",stdin); read(n); go(i,1,n) read(p[i]); go(i,1,n){ idx[(p[i]-i+n)%n]++; if(p[i]>i)cp++; if(p[i]<=i)cn++; sum+=abs(p[i]-i); } mink=0; ans=sum; go(k,1,n-1){ sum=sum+cn-1-cp; sum+=2*p[(n-k)%n+1]-1-n; cp++,cn--; cp-=idx[k],cn+=idx[k]; if(sum<ans){ mink=k; ans=sum; } } cout<<ans<<' '<<mink; return 0; }