题解 P4331 【[BalticOI 2004]Sequence 数字序列】

题目链接

Solution [BalticOI 2004]Sequence 数字序列

题目大意:给定一个长为 (n) 的整数序列 (a),你需要构造出一个序列 (b),满足 (forall i in [1,n)quad b_i<b_{i+1}),在这个前提下,使得 (sum |a_i-b_i|) 最小

可并堆,思维


分析:

有一个结论可以将严格递增变为不降。

考虑对于位置 (i,j),它们之间都是严格单调递增的,那么有(b_j-b_i geq j-i)

移项发现,将每个位置的数减去它们的下标,就转化为求一个不降的序列 (b)

有个十分暴力的 (dp),设 (f[i][j]) 表示构造 (b) 的前 (i) 个数,第 (i) 个数不超过 (j) 的最小代价和

(f[i][j]=min{f[i-1][k]+|a_i-k|})

由归纳法可以证明,对于一个位置 (i),我们的决策相当于是在求

(|x-a_1|+|x-a_2|+cdots|x-a_i|) 这个东西的最小值

由它的几何意义,我们可以知道取中位数最优。

但是全局取一个中位数作为 (b) 不一定是最优解,因为我们可以还可以将 (a) 划分为许多段,每段取一个中位数作为这一段的 (b),只要满足 (b) 单调不降就可以了

这个东西可以用一个大根堆来维护,因为求一个长为 (len) 的序列的中位数,就相当于求它的第 (lceil frac{len}{2} ceil)

那么我们开一个栈,每个元素是一个堆。每次插入一个元素 (a),就看看它作为中位数是否满足单调不降,不满足就弹栈,合并了再压回去。用左偏树来实现就可以了。

#include <cstdio>
#include <cctype>
#include <stack>
#include <utility>
#include <algorithm>
#pragma GCC optmize(2)
using namespace std;
typedef long long ll;
constexpr int maxn = 1e6 + 100,inf = 0x7fffffff;
struct IO{//-std=c++11,with cstdio and cctype
	private:
		static constexpr int ibufsiz = 1 << 20;
		char ibuf[ibufsiz + 1],*inow = ibuf,*ied = ibuf;
		static constexpr int obufsiz = 1 << 20;
		char obuf[obufsiz + 1],*onow = obuf;
		const char *oed = obuf + obufsiz;
	public:
		inline char getchar(){
			#ifndef ONLINE_JUDGE
				return ::getchar();
			#else
				if(inow == ied){
					ied = ibuf + sizeof(char) * fread(ibuf,sizeof(char),ibufsiz,stdin);
					*ied = '';
					inow = ibuf;
				}
				return *inow++;
			#endif
		}
		template<typename T>
		inline void read(T &x){
			static bool flg;flg = 0;
			x = 0;char c = getchar();
			while(!isdigit(c))flg = c == '-' ? 1 : flg,c = getchar();
			while(isdigit(c))x = x * 10 + c - '0',c = getchar();
			if(flg)x = -x;
		}
		template <typename T,typename ...Y>
		inline void read(T &x,Y&... X){read(x);read(X...);}
		inline int readi(){static int res;read(res);return res;}
		inline long long readll(){static long long res;read(res);return res;}
		
		inline void flush(){
			fwrite(obuf,sizeof(char),onow - obuf,stdout);
			fflush(stdout);
			onow = obuf;
		}
		inline void putchar(char c){
			#ifndef ONLINE_JUDGE
				::putchar(c);
			#else
				*onow++ = c;
				if(onow == oed){
					fwrite(obuf,sizeof(char),obufsiz,stdout);
					onow = obuf;
				}
			#endif
		}
		template <typename T>
		inline void write(T x,char split = ''){
			static unsigned char buf[64];
			if(x < 0)putchar('-'),x = -x;
			int p = 0;
			do{
				buf[++p] = x % 10;
				x /= 10;
			}while(x);
			for(int i = p;i >= 1;i--)putchar(buf[i] + '0');
			if(split != '')putchar(split);
		}
		inline void lf(){putchar('
');}
		~IO(){
			fwrite(obuf,sizeof(char),onow - obuf,stdout);
		}
}io;
template <typename A,typename B>
inline void chkmin(A &x,const B &y){if(y < x)x = y;}
template <typename A,typename B>
inline void chkmax(A &x,const B &y){if(y > x)x = y;}

int n,a[maxn],b[maxn];
struct node{
	int ls,rs,v,dis,siz;
}tr[maxn];
inline int merge(int x,int y){
	if(!x || !y)return x | y;
	if(tr[x].v < tr[y].v)swap(x,y);
	tr[x].rs = merge(tr[x].rs,y);
	if(tr[tr[x].ls].dis < tr[tr[x].rs].dis)swap(tr[x].ls,tr[x].rs);
	tr[x].dis = tr[tr[x].rs].dis + 1;
	tr[x].siz = tr[tr[x].ls].siz + tr[tr[x].rs].siz + 1;
	return x;
}
stack<pair<int,int>> stk;
int main(){
#ifndef ONLINE_JUDGE
	freopen("fafa.in","r",stdin);
#endif
	io.read(n);
	for(int i = 1;i <= n;i++)
		io.read(a[i]),a[i] -= i;
	for(int i = 1;i <= n;i++){
		tr[i].v = a[i],tr[i].siz = 1;
		auto now = make_pair(i,1);
		while(!stk.empty() && tr[stk.top().first].v > tr[now.first].v){
			now.second += stk.top().second;
			now.first = merge(now.first,stk.top().first);
			stk.pop();
			while(tr[now.first].siz > ((now.second + 1) >> 1))now.first = merge(tr[now.first].ls,tr[now.first].rs);
		}
		stk.push(now);
	}
	int pos = n;
	while(!stk.empty()){
		auto now = stk.top();stk.pop();
		while(now.second--)b[pos--] = tr[now.first].v;
	}
	ll ans = 0;
	for(int i = 1;i <= n;i++)ans += abs((ll)a[i] - b[i]);
	io.write(ans,'
');
	for(int i = 1;i <= n;i++)io.write(b[i] + i,' ');
	io.lf();
	return 0;
}
原文地址:https://www.cnblogs.com/colazcy/p/14074645.html