CF819B Mister B and PR Shifts
思路分析:其实这道题的暴力是十分好想的,就是硬模拟,但是看到这个n的取值范围......你懂我意思吧。
我们可以先假设有一个序列为2,1,3,2,5,那么我们如果不做处理,那么他们的值减去坐标就分别为:1,-1,0,-2,0。我们现在将最后一个数放在前面,序列变为了5,2,1,3,2。不做处理的值为:4,0,-2,-1,-3。我们发现,除了放在最前面的数字之外其余的数字都在原基础上减去了1,由于最终答案求的是绝对值,那么减1后原来是负数的数对答案的贡献会变大(因为绝对值变大了),原来是正数的数对答案的贡献会变小。于是我们只需要在模拟过程中记录负数和正数的个数,再在上一状态的答案上进行修改就可以了,需要注意的是当一个负数从系列尾部放到序列首部时可能会导致它变为负数,一个正数过程中可能变为负数。
我们照着上面的思路写肯定是可以的,但是蒟蒻写了半天写出来了我都不愿看的及其复杂的码,网上的神犇们都是分别统计的正数贡献和负数贡献,用count来记录正数何时减成负数之后直接加和和ans对比,比蒟蒻的代码不知强了多少倍。。。。
网上神犇的思路的码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<cmath> 5 using namespace std; 6 const int N=2000000+3; 7 typedef long long ll; 8 ll n,a[N],sum_postive,sum_negative,pos,ans,num_postive,num_negative,Count[N]; 9 int main(){ 10 scanf("%lld",&n); 11 for(ll i=1;i<=n;i++){ 12 scanf("%lld",&a[i]); 13 if(a[i]<=i){ 14 num_negative++; //负数个数及贡献 15 sum_negative+=i-a[i]; 16 } 17 else{ 18 num_postive++; //正数个数及贡献 19 sum_postive+=a[i]-i; 20 Count[a[i]-i]++; //记录几轮后正值变为负数 21 } 22 } 23 ans=sum_postive+sum_negative;//不处理答案 24 pos=0; 25 for(ll i=1;i<=n-1;i++){ 26 sum_postive-=num_postive;//每一轮更新 27 num_postive-=Count[i];//看正数是否减少 28 sum_negative+=num_negative;//负数贡献加上 29 num_negative+=Count[i]; 30 sum_negative-=n+1-a[n-i+1];//正数减去 31 num_negative--; 32 if(a[n-i+1]>1){//看序列尾部元素是否转正 33 Count[a[n-i+1]-1+i]++; 34 sum_postive+=a[n-i+1]-1; 35 num_postive++; 36 } 37 else num_negative++; 38 if(sum_negative+sum_postive<ans){ //比较答案 39 ans=sum_negative+sum_postive; 40 pos=i; 41 } 42 } 43 printf("%lld %lld",ans,pos); 44 return 0; 45 }
蒟蒻的码.....算了应该也没人愿意看。