• 【bzoj1112】[POI2008]砖块Klo Treap


    题目描述

    N柱砖,希望有连续K柱的高度是一样的. 你可以选择以下两个动作 1:从某柱砖的顶端拿一块砖出来,丢掉不要了. 2:从仓库中拿出一块砖,放到另一柱.仓库无限大. 现在希望用最小次数的动作完成任务.

    输入

    第一行给出N,K. (1 ≤ k ≤ n ≤ 100000), 下面N行,每行代表这柱砖的高度.0 ≤ hi ≤ 1000000

    输出

    最小的动作次数

    样例输入

    5 3
    3
    9
    2
    3
    1

    样例输出

    2


    题解

    Treap

    根据某数学定理,∑|ai-x|最小时x为ai的中位数。

    那么我们可以对每段可能的区间取一下中位数,再查找这个最小和。

    具体实现方法可以使用Treap,同时维护si和sum。

    求最小和时需要递归进行,比较x和v[k]的大小关系,如果有一整颗子树的值都小于或大于x,那么直接加上|sum-si*x|,并递归处理剩余部分。

    时间好像有点长,大概5s,但这样的做法保证能过。

    #include <cstdio>
    #include <cstdlib>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    int cnt[100010] , rnd[100010] , l[100010] , r[100010] , si[100010] , root , tot;
    ll a[100010] , v[100010] , sum[100010];
    void pushup(int x)
    {
    	si[x] = si[l[x]] + si[r[x]] + cnt[x];
    	sum[x] = sum[l[x]] + sum[r[x]] + v[x] * cnt[x];
    }
    void zig(int &k)
    {
    	int t = l[k];
    	l[k] = r[t];
    	r[t] = k;
    	pushup(k);
    	pushup(t);
    	k = t;
    }
    void zag(int &k)
    {
    	int t = r[k];
    	r[k] = l[t];
    	l[t] = k;
    	pushup(k);
    	pushup(t);
    	k = t;
    }
    void ins(int &k , ll x)
    {
    	if(!k) k = ++tot , v[k] = sum[k] = x , cnt[k] = si[k] = 1 , rnd[k] = rand(); 
    	else if(x == v[k]) cnt[k] ++ ;
    	else if(x < v[k])
    	{
    		ins(l[k] , x);
    		if(rnd[l[k]] < rnd[k]) zig(k);
    	}
    	else
    	{
    		ins(r[k] , x);
    		if(rnd[r[k]] > rnd[k]) zag(k);
    	}
    	pushup(k);
    }
    void del(int &k , ll x)
    {
    	if(x == v[k] && cnt[k] <= 1)
    	{
    		if(l[k] * r[k] == 0) k = l[k] + r[k];
    		else if(rnd[l[k]] < rnd[r[k]]) zig(k) , del(k , x);
    		else zag(k) , del(k , x);
    		return;
    	}
    	if(x == v[k]) cnt[k] -- ; 
    	else if(x < v[k]) del(l[k] , x);
    	else del(r[k] , x);
    	pushup(k);
    }
    ll find(int k , int x)
    {
    	if(x <= si[l[k]]) return find(l[k] , x);
    	else if(x > si[l[k]] + cnt[k]) return find(r[k] , x - si[l[k]] - cnt[k]);
    	else return v[k];
    }
    ll query(int k , ll x)
    {
    	if(!k) return 0;
    	if(x == v[k]) return x * si[l[k]] - sum[l[k]] + sum[r[k]] - x * si[r[k]];
    	else if(x < v[k]) return (v[k] - x) * cnt[k] + sum[r[k]] - x * si[r[k]] + query(l[k] , x);
    	else return (x - v[k]) * cnt[k] + x * si[l[k]] - sum[l[k]] + query(r[k] , x);
    }
    int main()
    {
    	int n , k , i;
    	ll ans = 0x7fffffffffffffffll;
    	scanf("%d%d" , &n , &k);
    	for(i = 1 ; i <= n ; i ++ ) scanf("%lld" , &a[i]);
    	for(i = 1 ; i < k ; i ++ ) ins(root , a[i]);
    	for(i = k ; i <= n ; i ++ )
    	{
    		ins(root , a[i]);
    		ans = min(ans , query(root , find(root , k / 2 + 1))) , del(root , a[i - k + 1]);
    	}
    	printf("%lld
    " , ans);
    	return 0;
    }

     

  • 相关阅读:
    find命令
    shell编程基础
    grep命令
    awk命令
    结对项目之需求分析与原型模型设计
    使用Git进行代码管理的心得
    软件工程的实践项目的自我目标
    第五次作业——团队项目——需求规格说明书
    调研android开发环境的发展演变
    结对编程总结
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/6655888.html
Copyright © 2020-2023  润新知