CodeForces-820D Mister B and PR Shifts

/*
  这题十分值得多多回顾,常拿出来重做一次
  这题真是想得极为艰难的一道题,不过想明白以后,发现其实还是挺有意思的(其实也不算是我想明白的,是请教了别人,再纠结了很久,不然以我的智商,别说自己想,看懂blog上的代码都相当艰难了...)...
  
  shift记录右移多少位可使 a[i] == i;确切说,下标为移动次数,值为移动下标次数时,满足 a[i] == i的个数 
  参考系一直是最初的数组,而不是改变后的数组
  
  用l来记录当前满足 a[i] > i的个数,则r == n - l(r在程序中没有写出,一直用n - l 来表达),之所以不取得a[i] >= i,是因为单独拿出等于的那种情况分析,由于后移,i变大,则 i - a[i]变大,a[i] <= i的两种情况一样,都是对绝对值总和的贡献度+1,a[i] < i则是对绝对值总和的贡献度-1,故而等于号应该放在r的那边
  
  每次输入,若a[i] > i,说明可通过右移最终实现相等,一方面累加l,一方面更新shift数组中的对应元素。此外,每次输入后,都要累加a[i]-i的绝对值...
  
  下一个for循环开始移动,每次移动,由于l和r的对应数据,分别导致贡献度-1和+1,所以d要减去l,加上r,但其实,是加上r-1,因为每次移动,最后的数据会被移动到最前,这个数据的贡献度变化量,是要单独算的,但我们之前在统计时,把它加到了r里(因为下标n,所有元素在1~n之间,满足 i >= a[i],恰好属于r对应的范围),所以要减去1
  
  而此时数列最后的元素移到最前,使得d变化 abs(x -1) - abs(x - n)
  
  此外,更新l,移动i次时,会有shift[i]个元素通过移动,达到元素和下标的相等,因此l要减去这些
  
  再对最后的元素x单独处理,如果它等于1,则它会算到r的阵营,否则,按照下标和数组元素的关系,它会算到l的阵营,使得l自增,这种情况下,对应的shift也要更新,x-1为元素与下标之差,为此次移动后,还需要的移动次数,再加上已经移动了的i次...并取模n,因为移动是有循环的,如果已经右移(n+i)次,也可直接当作右移i次
  
   这里,其实我有怀疑过,可能因为上一步的shift更新,导致某些数组元素,它计算过两次shift,但这无伤大雅,因为,画图感受下,如果之前就是a[i] > i,两次算出的右移次数,也就(n+i)次和i次的区别...而取模后就没有区别了
   
   但是这步也一定要留下,别忘了可是也有a[i] < i的,它们若想相等,就必须位置先移到尾端,再从最前的1开始,才有可能实现...这里单独处理x时对shift的更新,恰恰就是为了这种情况,原来不能通过右移实现a[i] == i的情况,现在通过右移到末端,再从最左开始,有可能实现了,它们的shift也终于可以更新了(只有通过右移可以实现时,才会更新shift,而对应的原来不满足的点,一旦移到最前端,就有机会满足了)
 */



#include <bits/stdc++.h>
typedef long long LL;
const int N = 1e6 + 10;
int a[N], shift [N]; // shift记录右移多少位可使 a[i] == i;确切说,下标为移动次数,值为移动下标次数时,满足 a[i] == i的个数 
using namespace std;
// shift记录右移多少位可使 a[i] == i;确切说,下标为移动次数,值为移动下标次数时,满足 a[i] == i的个数 
int main()
{
	int n;
	while (cin >> n)
	{
		memset( shift, 0, sizeof(shift) );
		LL d = 0;
		int l = 0; // l 用来记录a[i] > i的个数,r = n - l
		for ( int i = 1; i <= n; i++ )
		{
			cin >> a[i];
			if (a[i] > i) l++, shift[ a[i] - i ]++;
			d += abs (a[i] - i);
		}
		
		LL mind = d;
		int mini = 0;
		for ( int i = 1; i < n; i++ )
		{
			int x = a[n - i + 1]; //第i次移动前,数组的最后一个元素 
			d += (n - l - 1) - l + abs(x - 1) - abs(x - n);
			
			l -= shift[i];
			
			if (x != 1)
			{
				l++;
				shift[(x - 1 + i) % n]++; 
			}
			
			if ( d < mind )
			{
				mind = d;
				mini = i;
			}
		}
		cout << mind << " " << mini << endl; 
	}
	return 0;
}


原文地址:https://www.cnblogs.com/mofushaohua/p/7789512.html