洛谷 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;
}
posted @ 2022-02-09 11:38  Reanap  阅读(166)  评论(1编辑  收藏  举报