洛谷 2 月月赛 I & WdOI R1.5 / RdOI R3.5 Mystery
好高妙啊!
肯定是把 \(a_i - i * k\) ,这样就消除了 \(k\) 的限制,只有单调的限制。
先讲讲我的垃圾做法。
考虑递推求解。我们把每一段相等的视作一组,然后显然呈阶梯状。
如果加入一个元素,他比最后一级阶梯高,我们就将它另开一级阶梯更优。
否则,将它与阶梯平齐一定能得到对于当前不劣的解(因为我们如果下降上一级台阶一次会带来-1的贡献,同时这一位会有1的贡献,答案不变),我们想要它对将来依然优秀,即让这级台阶尽可能低,显然就是下降到较小中位数的位置,但可能不满足阶梯性质,于是可以合并两级阶梯。发现合并之后往下移答案会变劣(r-1 级下移 -1 贡献,r 级下移 0 贡献),所以每次合并最多合并一次。
对顶堆维护每段中位数实现,启发式合并可以做到小常数 \(O(n \log^2 n)\) ,可过。
主席树应该可以单 \(O(n\log n)\)。
然后就是发现了一个高妙做法。
其与我本质不同在于答案计算。
考虑我做法的合并部分,合并的数一大一小配对,除去新加的数都有:大的比上级台阶大,小的比上级台阶小,那直接把答案加上大小之差相当于就加进去了(也可视作没加进去,视作加进去了中位数也没变)。
最后可能会剩下一个数,比阶梯小。加进阶梯对答案的贡献就是那个阶梯的最小中位数与该数的距离,因为加进去后它还是中位数。
于是我们需要一个数据结构去维护中位数。发现我们的操作相当于对一个集合不断加入比中位数小的数。那么每个数只有两次机会作为最小中位数去计算答案(一次在中靠下,一次在中间)。那么我们把每个元素加进大根堆两次,根头就是我们当前最高阶梯的中位数,算一次答案就可以 pop 掉。
相当巧妙,不论是实现上还是想法上。比 1A 高明到不知哪里去了。
代码注释掉的是我的初做法:
/*
好高妙啊
*/
#include <map>
#include <set>
#include <queue>
#include <cmath>
#include <bitset>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define pii pair <int , int>
#define mp make_pair
#define fs first
#define sc second
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
//const int Mxdt=100000;
//static char buf[Mxdt],*p1=buf,*p2=buf;
//#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,Mxdt,stdin),p1==p2)?EOF:*p1++;
template <typename T>
void read(T &x) {
T f=1;x=0;char s=getchar();
while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=(x<<3)+(x<<1)+(s-'0');s=getchar();}
x *= f;
}
template <typename T>
void write(T x , char s='\n') {
if(!x) {putchar('0');putchar(s);return;}
if(x<0) {putchar('-');x=-x;}
T tmp[25]={},t=0;
while(x) tmp[t++]=x%10,x/=10;
while(t-->0) putchar(tmp[t]+'0');
putchar(s);
}
const int MAXN = 1e6 + 5;
LL n , k;
LL a[MAXN];
//struct Op_Heap {
// priority_queue <LL , vector <LL> , greater <LL> > up;//up.size()==dw.size()||up.size()==dw.size()-1
// priority_queue <LL , vector <LL> , less <LL> > dw;
// LL Sum_up , Sum_dw;
// void Ins(LL x) {
// if(up.empty() || x <= up.top()) dw.push(x) , Sum_dw += x;
// else up.push(x) , Sum_up += x;
//
// while(up.size() + 1 < dw.size()) up.push(dw.top()) , Sum_up += dw.top() , Sum_dw -= dw.top() , dw.pop();//up >= dw - 1
// while(up.size() > dw.size()) dw.push(up.top()) , Sum_dw += up.top() , Sum_up -= up.top() , up.pop();//up <= dw
// //dw >= up >= dw - 1
// }
// LL calc() {return Sum_up - dw.top() * up.size() + dw.top() * dw.size() - Sum_dw;}
// void merge(Op_Heap &H) {
// if(up.size() + dw.size() < H.up.size() + H.dw.size()) {
// swap(up , H.up);swap(Sum_up , H.Sum_up);
// swap(dw , H.dw);swap(Sum_dw , H.Sum_dw);
// }
// while(H.dw.size()) Ins(H.dw.top()) , H.dw.pop();
// while(H.up.size()) Ins(H.up.top()) , H.up.pop();
// H.Sum_dw = H.Sum_up = 0;
// }
//}Q[MAXN];
LL val[MAXN];
int tp , T;
int main() {
read(n),read(k);
for (int i = 1; i <= n; ++i) read(a[i]) , a[i] -= (i - 1) * k;
read(T);
// tp = 1;val[1] = a[1];Q[1].Ins(a[1]);
// LL cur = 0;
// if(!T) write(cur);
// for (int i = 2; i <= n; ++i) {
// if(a[i] > val[tp]) tp ++ , val[tp] = a[i] , Q[tp].Ins(a[i]);
// else {
// cur -= Q[tp].calc();
// Q[tp].Ins(a[i]);
// cur += Q[tp].calc();
// if(tp > 1 && Q[tp].dw.top() <= val[tp - 1]) cur -= Q[tp].calc() , cur -= Q[tp - 1].calc() , Q[tp - 1].merge(Q[tp]) , tp -- , cur += Q[tp].calc();
// val[tp] = Q[tp].dw.top();
// }
// if(!T || i == n) write(cur);
// }
priority_queue <LL , vector <LL> , less <LL> > q;
LL ans = 0;
for (int i = 1; i <= n; ++i) {
q.push(a[i]),q.push(a[i]);
ans += q.top() - a[i];q.pop();
if(!T || i == n) write(ans);
}
return 0;
}