解题报告
题目
传送门:https://www.luogu.com.cn/problem/CF819B
题意翻译
- 定义一个全排列 p~i~ 的偏移值为(sum_{i=1}^n)| p~i~ - i |
- 给你一个全排列,你可以从后面拿k∈[0,n−1]个数放在前面,使得该全排列的偏移值最小,输出这个偏移值和k,如果有多个k任意输出一个
- n ≤ 10^6^
开整
(不看翻译是真的看不懂……)
上来一看以为要用差分,莽一波欢乐报零。于是换个思路想一想。
就暴力而言,我们是从后往前,一个一个拿到最前面,然后再扫一遍判断ans的变化。
于是乎,转念一想,我们当前位的数的本身的大小是不变的,而我们每操作一次它的编号 i 就会增大1 。
那么,对于原先偏移值(注意这个“原”是对于当前次操作而言,而并非是最开始的那个,下面的也是一样,我们的偏移值是在随着操作不断改变的)为正数的,显然对ans的贡献量会减少1,即ans一共会减少原偏移值为正的数的个数。
对于原偏移值为非正的,显然对ans的贡献会加1,所以ans的增加量是原偏移值为非正数的数的个数。
所以,我们要维护的就是原偏移值为正的和非正的数的个数。
然后我们就可以考虑了,经过一次操作,原偏移值为+1的数偏移值都会变为0,也就是说,原偏移值为正数的数的个数会少掉原偏移值为+1的数的个数。
而原偏移值为非正数的数的个数会加上原偏移值为+1的数的个数,这也是显然的。所以原偏移值为非正的数的个数不会变少,只会越来越多。
另外,每次操作我们把第 n-i+1 位的数放到最前面,这个数所带来的偏移值的改变直接计算即可。
这样我们整个时间效率应该是稳定的O(2n),然后这题最好别用cin,cout...虽然能过,但是我加优化都跑了15s...
然后,就这样。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#define ll long long
using namespace std;
char buf[1 << 20], *p1 = buf, *p2 = buf;
char getc() {
if(p1 == p2) {
p1 = buf;
p2 = buf + fread(buf, 1, 1 << 20, stdin);
if(p1 == p2) return EOF;
}
return *p1++;
}
inline int read() {
int s = 0, w = 1;
char c = getc();
while(c < '0' || c > '9') {if(c == '-')w = -1; c = getc();}
while(c >= '0' && c <= '9') s = s * 10 + c - '0', c = getc();
return s * w;
}
const int maxn = 3e6 + 10;
int a[maxn];
ll ans = 0, k = 0;
ll zheng_cnt, fu_cnt;
ll one[maxn];//第i次操作时原偏移值为1的数的个数就是one[i],大家想一想就知道了。
int main(){
int n = read();
for(int i = 1; i <= n; i++) {
a[i] = read();
int x = a[i] - i;
if(x <= 0) {
fu_cnt++;
ans += abs(x);
}
else {
zheng_cnt++;
ans += x;
one[x]++;
}
}
ll tmp, tmpp = ans;//为了避免错误地修改ans,咱们用两个中间变量跑循环
if(ans == 0) {//一些剪枝优化,但感觉没什么卵用
printf("0 0
");
return 0;
}
for(int i = 1; i < n; i++){//没什么好说的了
tmp = tmpp;
tmp -= zheng_cnt;
tmp += fu_cnt;
zheng_cnt -= one[i];
fu_cnt += one[i];
//对这个挪动的数进行操作
ll x = a[n - i + 1];
tmp -= n + 1 - x;
fu_cnt--;
if(x > 1) {
one[x - 1 + i]++;
tmp += x - 1;
zheng_cnt++;
}
else fu_cnt++;
//操作结束
if(tmp < ans) {
ans = tmp;
k = i;
}
if(ans == 0) {//还是剪枝,依然是没什么卵用
printf("0 %lld
", k);
return 0;
}
tmpp = tmp;//记得循环一下变量,不然整个过程就不是连续的了
}
printf("%lld %lld
", ans, k);
return 0;
}