POI 2000 Promotion 促销活动

数据结构问题,双向优先队列维护,每次插入一些元素,取出最大值和最小值。可以用两个堆维护,在一个堆中删除后相应在另一个堆中也删除。也可以用平衡树,下面是我的Treap的代码。

/* 
 * Problem: POI2000 pro
 * Author: Guo Jiabao
 * Time: 2008.12.22 20:35
 * State: Solved
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>

using namespace std;

class tTreap
{
    private:
        class tNode
        {
            public:
                tNode *left,*right;
                int v,fix;
                tNode(int tv): v(tv),left(0),right(0)
                {
                    fix=rand();
                }
        };
        tNode *root;
        int R;
        void rot_r(tNode *&P)
        {
            tNode *Q=P->left;
            P->left=Q->right;
            Q->right=P;
            P=Q;
        }
        void rot_l(tNode *&P)
        {
            tNode *Q=P->right;
            P->right=Q->left;
            Q->left=P;
            P=Q;
        }
        void _ins(tNode *&P,int v)
        {
            if (!P)
                P=new tNode(v);
            else if (v<P->v)
            {
                _ins(P->left,v);
                if (P->left->fix < P->fix)
                    rot_r(P);
            }
            else
            {
                _ins(P->right,v);
                if (P->right->fix < P->fix)
                    rot_l(P);
            }
        }
        void _delete_max(tNode *&P)
        {
            if (P->right)
                _delete_max(P->right);
            else
            {
                R=P->v;
                tNode *T=P;
                P=P->left;
                delete T;
            }
        }
        void _delete_min(tNode *&P)
        {
            if (P->left)
                _delete_min(P->left);
            else
            {
                R=P->v;
                tNode *T=P;
                P=P->right;
                delete T;
            }
        }
    public:
        tTreap():root(0){}
        void ins(int v){_ins(root,v);}
        int delete_max() {_delete_max(root);return R;}
        int delete_min() {_delete_min(root);return R;}
};

int N,Ans;
tTreap Treap;

void init()
{
    freopen("pro.in","r",stdin);
    freopen("pro.out","w",stdout);
}

void solve()
{
    int M,v,A,B;
    scanf("%d",&N);
    for (int i=1;i<=N;i++)
    {
        scanf("%d",&M);
        for (int j=1;j<=M;j++)
        {
            scanf("%d",&v);
            Treap.ins(v);
        }
        A=Treap.delete_max();
        B=Treap.delete_min();
        Ans+=A-B;
    }
}

int main()
{
    init();
    solve();
    printf("%d",Ans);
    return 0;
}
<h2><span class="mw-headline">促销活动</span></h2>

问题描述

促销活动遵守以下规则:
<ol>
    <li>一个消费者 —— 想参加促销活动的消费者,在账单下记下他自己所付的费用,他个人的详细情况,然后将账单放入一个特殊的投票箱。</li>
    <li>当每天促销活动结束时,从投票箱中抽出两张账单:</li>
</ol>
  • 第一张被抽出的账单是金额最大的账单
  • 然后被抽出的是金额最小的账单,对于付了金额最大账单的这位消费者,将得到一定数目的奖金,其奖金数等于他账单上的金额与选出的最小金额的差。
  • 为了避免一个消费者多次获奖,根据上面所抽出的两张账单都不返回到投票箱,但是剩下的账单还继续参加下一天的促销活动。

    超市的售出额是巨大的,这样可以假定,在每天结束,拿出数额最大账单和数额最小账之前,在投票箱内就已经至少存在了 2 张账单。你的任务是去计算每天促销活动投进投票箱的账单数额的基本信息。在整个活动中开销总数。

    本题中约定:

    整个活动持续了 N 天 (N<=5000) 。 第 i 天放入的帐单有 a[i] 张, a[i]<=10^5 。且 sigma(a[1]...a[n])<=10^6 。 每一天放入的帐单的面值均 <=10^6 。

    输入格式

    输入文件的第一行是一个整数 n ( 1 <= n <= 5000 ),表示促销活动历时的天数。

    以下的 n 行,每行包含若干由空格分隔的非负整数。第 i+1 行的数表示在第 i 天投入箱子的账单金额。每行的第一个数是一个整数 k ( 0 <= k <= 10^5 ), 表示当日账单的数目。后面的 k 个正整数代表这 k 笔账单的金额,均小于10^6 。

    整个活动中涉及到的账单笔数不会超过 10^6 。

    输出格式

    输出文件的唯一一行是一个整数,等于整个促销活动中应该付出的奖金总额。

    【输入输出样例】

    输入

    5
      3 1 2 3
      2 1 1
      4 10 5 5 1
      0
      1 2
    输出
    19

相关日志