zoukankan      html  css  js  c++  java
  • BZOJ1367【Baltic2004】sequence

    题面

    Description

    Description

    Input

    Input

    Output

    一个整数R

    Sample Input

    7
    9
    4
    8
    20
    14
    15
    18
    

    Sample Output

    13
    

    Hint

    所求的Z序列为6,7,8,13,14,15,18.
    R=13

    Solution

    我们首先来考虑另一个问题: 给定一个数列({a_n}), 求一个单调不下降的({b_n}), 使得(sum |b_n - a_n|)最小.
    考虑两种较为特殊情况:

    • (a_1 le a_2 le ... le a_n), 此时(b_n = a_n)
    • (a_1 ge a_2 ge ... ge a_n), 此时(b_n = {a_n}的中位数)

    不难发现, 假如我们把({a_n})单调不下降的情况看作是一个数一段, 则它与({a_n})单调不上升的情况是等价的.
    因此, 这道题目的做法就是: 从前往后分段, 对于(a_n)这一个数, 开始时我们把它单独作为一段, 假如这一段的中位数比上一段要小, 则把当前一段和上一段合并. 直至当前(a_n)所在段的中位数大于等于上一段的中位数或只剩下一段. 然后考虑数列上的下一个数.
    回到原题, 由于原题要求(z_n < z_{n + 1}), 我们把读入的(t_n)变成(b_n - n), 再按照上述方法求解即可.
    考虑如何动态维护中位数: 比较简单的想法就是启发式合并平衡树, 时间复杂度(O(n log^2 n)), 会TLE. 下面是代码.

    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    #include <cmath>
    
    namespace Zeonfai
    {
        inline int getInt()
        {
            int a = 0, sgn = 1;
            char c;
     
            while(! isdigit(c = getchar()))
                if(c == '-')
                    sgn *= -1;
     
            while(isdigit(c))
                a = a * 10 + c - '0', c = getchar();
     
            return a * sgn;
        }
    }
    
    const int N = (int)1e6;
    int a[N], cnt;
    
    struct section
    {
    	int L, R, w;
    }sec[N];
    
    struct splayTrees
    {
    	struct node
    	{
    		int suc[2], pre, sz, w;
    	}nd[N];
    
    	int rt[N];
    
    	inline void newNode(int u, int w)
    	{
    		nd[u].w = w, nd[u].suc[0] = nd[u].suc[1] = nd[u].pre = -1, nd[u].sz = 1;
    	}
    
    	inline void update(int u)
    	{
    		nd[u].sz = 1;
    
    		for(int i = 0; i < 2; ++ i)
    			if(~ nd[u].suc[i])
    				nd[u].sz += nd[nd[u].suc[i]].sz;
    	}
    
    	inline int getRelation(int u)
    	{
    		if(! (~ nd[u].pre))
    			return -1;
    
    		return u == nd[nd[u].pre].suc[1];
    	}
    
    	inline void rotate(int u)
    	{
    		int pre = nd[u].pre, prepre = nd[pre].pre, k = getRelation(u);
    		
    		if(~ nd[u].suc[k ^ 1])
    			nd[nd[u].suc[k ^ 1]].pre = pre;
    		
    		nd[pre].suc[k] = nd[u].suc[k ^ 1];
    		nd[u].suc[k ^ 1] = pre;
    		nd[u].pre = nd[pre].pre;
    
    		if(~ prepre)
    			nd[prepre].suc[getRelation(pre)] = u;
    
    		nd[pre].pre = u;
    		update(pre), update(u);
    	}
    
    	inline void splay(int a, int u)
    	{
    		while(~ nd[u].pre)
    		{
    			int pre = nd[u].pre;
    
    			if(~ nd[pre].pre)
    				rotate(getRelation(pre) == getRelation(u) ? pre : u);
    
    			rotate(u);
    		}
    
    		rt[a] = u;
    	}
    
    	void _insert(int a, int u, int v)
    	{
    		++ nd[u].sz;
    
    		if(nd[v].w < nd[u].w)
    		{
    			if(! (~ nd[u].suc[0]))
    			{
    				newNode(v, nd[v].w);
    				nd[v].pre = u;
    				nd[u].suc[0] = v;
    				splay(a, v);
    			}
    			else 
    				_insert(a, nd[u].suc[0], v);
    		}
    		else
    		{
    			if(! (~ nd[u].suc[1]))
    			{
    				newNode(v, nd[v].w);
    				nd[v].pre = u;
    				nd[u].suc[1] = v;
    				splay(a, v);
    			}
    			else
    				_insert(a, nd[u].suc[1], v);
    		}
    	}
    
    	inline void insert(int a, int u)
    	{
    		_insert(a, rt[a], u);
    	}
    
    	void _merge(int a, int u)
    	{
    		int suc[2] = {nd[u].suc[0], nd[u].suc[1]};
    		insert(a, u);
    
    		for(int i = 0; i < 2; ++ i)
    			if(~ suc[i])
    				_merge(a, suc[i]);
    	}
    
    	inline int merge(int a, int b)
    	{
    		if(nd[rt[a]].sz < nd[rt[b]].sz)
    			std::swap(rt[a], rt[b]);
    
    		_merge(a, rt[b]);
    	}
    
    	int get(int u, int k)
    	{
    		if(~ nd[u].suc[0])
    		{
    			if(nd[nd[u].suc[0]].sz > k)
    				return get(nd[u].suc[0], k);
    			else
    				k -= nd[nd[u].suc[0]].sz;
    		}
    
    		if(! k)
    			return nd[u].w;
    		
    		return get(nd[u].suc[1], k - 1);
    	}
    
    	inline int getMedian(int a)
    	{
    		return get(rt[a], nd[rt[a]].sz >> 1);
    	}
    }trees;
    
    int main()
    {
    	#ifndef ONLINE_JUDGE
    	freopen("BZOJ1367.in", "r", stdin);
    	freopen("BZOJ1367.out", "w", stdout);
    	#endif
    
    	using namespace Zeonfai;
    
    	int n = getInt();
    
    	for(int i = 0; i < n; ++ i)
    		trees.newNode(i, a[i] = getInt() - i);
    
    	cnt = 0;
    
    	for(int i = 0; i < n; ++ i)
    	{
    		trees.rt[cnt] = i;
    		sec[cnt].w = a[i], sec[cnt].L = i, sec[cnt ++].R = i;
    
    		for(; cnt > 1 && sec[cnt - 2].w > sec[cnt - 1].w; -- cnt)
    			trees.merge(cnt - 2, cnt - 1), sec[cnt - 2].R = sec[cnt - 1].R, sec[cnt - 2].w = trees.getMedian(cnt - 2);
    	}
    
    	long long ans = 0;
    
    	for(int i = 0; i < cnt; ++ i)
    		for(int j = sec[i].L; j <= sec[i].R; ++ j)
    			ans += abs(a[j] - sec[i].w);
    
    	printf("%lld", ans);
    }
    
    

    正解是左偏树维护中位数, (O(n log n))

    因为我们合并的前提是:中位数(i)>中位数(i+1),那么对于合并后的i而言,中位数肯定是不升的
    根据这个性质我们又可以用可并堆了,堆顶元素表示该序列中的中位数
    当堆的元素个数*2>序列长度+1的时候就可以弹出堆顶

    懒得写代码了.

  • 相关阅读:
    DB2高可用hadr搭建参数配置
    redis一主两从搭建
    hdu 1064 Financial Management(超级大水题)
    hdu 1009 FatMouse' Trade(贪心水题)
    文件选择性加密解密
    uva 10405 Longest Common Subsequence(最长公共子序列)
    UVa 111 History Grading (最长公共子序列)
    hdu 2550 百步穿杨(大水题)
    UVa 10066 The Twin Towers(LCS水题)
    ASP.NET学习参考站点
  • 原文地址:https://www.cnblogs.com/ZeonfaiHo/p/6882341.html
Copyright © 2011-2022 走看看