这学期暑假集训的第一次博客。 ps:貌似某人好久没有写博客了吧
Codeforces Round #421 (Div. 2)/D
题意:
有n个数,从1到n乱序排列,定义这n个数的秩序值为∑(a[i]-i) (1<=i<=n), 你每次将这个数组向右循环移位p次,
问p等于多少时,这n个数的秩序值最小?
暴力的方法很好想,n*n遍历一下,因为n = 1e6,这时候就要换成 nlog[n]或者n的复杂度才行。其实这道题目就可以从复杂度出发,仔细思考有没有哪里可以降复杂度的。
最后我是参考了一位大神的做法,cf题解全英文还很长看的没有耐心。。
贴上他的做法(@Jaihk662):
记录下每个数是在目标位置的左边还是右边,存下所有在目标左边的数
cur[i]表示初始数组中有多少个数在它目标左边第i个位置上
->注意要存下一开始在目标左边(包括在目标上)的个数L,以及目标右边的数的个数r
之后直接模拟右移,对于第i次右移,L = L-cur[i-1], r = r+cur[i-1],即当次位移刚好有cur[i-1]个数原本在目标位置左边及目标上跑到了目标位置的右边,
每次的秩序值便是初始秩序值-L+r+特判最后一个数移到第一个数所产生的影响
但注意计算之后L还要+1,r还要-1因为最后一个数跑到了第一个
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <stack>
#include <bitset>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
#define clr(a,b) memset(a,b,sizeof(a))
#define pb(a) push_back(a)
#define fir first
#define se second
#define LL long long
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int maxn = 1e6+5;
const LL inf = 0x3f3f3f3f3f3f3f3f;
LL mod = 1e9+7;
double eps = 0.00000001;
double PI = acos(-1);
int p[maxn],pl[2*maxn]; //p是初始串,pl是 p[i]比i大了多少的个数
int main() {
int n;
scanf("%d",&n);
for(int i = 1;i <= n;i++) scanf("%d",&p[i]);
LL sum = 0,ans;
int pos = 0;
clr(pl,0);
int l = 0,r = 0;
for(int i = 1;i <= n;i++) {
sum += abs(p[i]-i);
if(p[i] >= i) {
l++;
pl[p[i]-i]++; //l存p[i]-i中大于i的个数,这种情况下大于的数字的个数
}
else r++;
}
ans = sum;
for(int i = 1;i < n;i++) {
l -= pl[i-1];
r += pl[i-1];
sum = sum - l + r - abs(n+1 - p[n-i+1]) + p[n-i+1]-1;
pl[p[n-i+1]-1+i]++;
if(sum < ans) {
ans = sum;
pos = i;
}
l++;
r--;
}
cout<<ans<<' '<<pos<<endl;
}
//每一次更新sum的值的时候只需要O(1),因为记录了差值,这样复杂度上就可以啦。
加油,机会总是给有准备的人留着。
fight!!!