hdu5261单调队列

题意特难懂,我看了好多遍,最后还是看讨论版里别人的问答,才搞明白题意,真是汗。

其实题目等价于给n个点,这n个点均匀分布在一个圆上(知道圆半径),点与点之间的路程(弧长)已知,点是有权值的,已知,点与点的距离等于其最短路程(弧长)加上两点的权值,问距离最远的两个点的下标。

因为是环状,不好处理,所以我在输入的时候就简单处理了一下,使问题变成直线上的等价问题了。做法就是在输入序列后面再加上前半段序列,例如样例5 2 1 10 1 10 10,可以处理成1 10 1 10 10 1 10,这样就只需要顺序处理了,不过最后输成的下标是需要处理的,因为要求的是字典序最小的下标对。

变成直线后的问题就比较简单了。便于理解的做法是维护一个长度为半圆的队列,当处理i时,把队列里的点和i点比较,更新最值,然后i入队,队列头元素出队。这是n^2的效率,换成单调队列,就能过了。不过,我是用优先队列做的,代码比用单调队列稍复杂一丁点,也是脑子木了,还没打完,金牛就把我代码要了去用单调队列接着打了。先贴上金牛的单调队列的代码,再贴我的,都是1a:

/*
 * Author : ben
 */
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <map>
#include <stack>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <functional>
#include <numeric>
#include <cctype>
using namespace std;

#define D(x) 

typedef long long LL;
const int MAXN = 200000;

int R, N;
LL m;
int data[MAXN];
LL ans;
int ansi, ansj;

void update(LL a, LL b)
{
    LL temp = (b - a) * R + data[a] + data[b];
    a %= N - m;
    b %= N - m;
    if (a > b)
        swap(a, b);
    if (temp > ans)
    {
    D(printf("a %lld b %lld\n", a, b));
        ans = temp;
        ansi = a;
        ansj = b;
        return;
    }
    if (temp < ans)
        return;
    if (ans == 40)
    if (ansi > a || (ansi == a && ansj > b))
    {
        ansi = a;
        ansj = b;
        return;
    }
}

LL work() {

    deque<LL> q;
    q.push_back(0);
    long long cur_pos = 0;
    long long ret = 0;
    for (int i = 1; i < N; i++)
    {
        cur_pos += R;
        while (!q.empty() && q.front() < i - m)
            q.pop_front();
        update(q.front(), i);
        while (!q.empty() && (i - q.back()) * R + data[q.back()] < data[i])
            q.pop_back();
        q.push_back(i);
    }
    return ret;
}

int main() {
    int T;
    scanf("%d", &T);
    for (int t = 1; t <= T; t++) {
        scanf("%d %d", &N, &R);
        for (int i = 0; i < N; i++) {
            scanf("%d", &data[i]);
        }
        m = N / 2;
        for (int i = 0; i < m; i++) {
            data[i + N] = data[i];
        }
        N += m;
        ans = 0;
        work();
        printf("Case #%d\n%d %d\n", t, ansi + 1, ansj + 1);
    }
    return 0;
}

下面是我的优先队列的:

/*
 * Author    : ben
 */
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <map>
#include <stack>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <functional>
#include <numeric>
#include <cctype>
using namespace std;
typedef long long LL;
const int MAXN = 200000;
typedef struct Mont {
    int height;
    int index;
    LL value;
    Mont(int ii = 0, int hh = 0, LL vv = 0) {
        height = hh;
        index = ii;
        value = vv;
    }
} Mont;
bool inline operator<(const Mont& m1, const Mont& m2) {
    return m1.value < m2.value;
}
LL R;
int N, m;
int data[MAXN];
LL ans;
int ansi, ansj;

inline void treat(int &i, int &j) {
    if (i >= N - m) {
        i = i % (N - m);
    }
    if (j >= N - m) {
        j = j % (N - m);
    }
    if (i > j) {
        int t = i;
        i = j;
        j = t;
    }
}

void work() {
    priority_queue<Mont> mont;
    mont.push(Mont(0, data[0], data[0]));
    for (int i = 1; i < N; i++) {
        while (mont.top().index < (i - m)) {
            mont.pop();
        }
        Mont topm = mont.top();
        LL tans = (i - topm.index) * R + data[i] + data[topm.index];
        if (tans > ans) {
            ans = tans;
            ansi = topm.index;
            ansj = i;
            treat(ansi, ansj);
        } else if(tans == ans) {
            int x = topm.index;
            int y = i;
            treat(x, y);
            if (x < ansi || (x == ansi && y < ansj)) {
                ansi = x;
                ansj = y;
            }
        }
        mont.push(Mont(i, data[i], data[i] - i * R));
    }
}

int main() {
    int T;
    scanf("%d", &T);
    for (int t = 1; t <= T; t++) {
        scanf("%d %lld", &N, &R);
        for (int i = 0; i < N; i++) {
            scanf("%d", &data[i]);
        }
        m = N / 2;
        for (int i = 0; i < m; i++) {
            data[i + N] = data[i];
        }
        N += m;
        ans = 0;
        work();
        printf("Case #%d:\n%d %d\n", t, ansi + 1, ansj + 1);
    }
    return 0;
}
时间: 2024-12-18 18:28:36

hdu5261单调队列的相关文章

hdu5261蜀道难 单调队列

题目:一个圆周被均匀分成n个点,每个点有一个高度h[i],定义两个点的距离是dis(i,j) = h[i] + h[j] + (劣弧i,j).问最长距离的点对,要求字典序最小. 单调队列:因为是圆周,要先把圆周变成链,倍增即可.在2n的链上,维护一个 h[i]-r*i 的单调递减队列,控制队列元素个数<=n/2,每次加进一个元素更新一次答案. 为什么要维护h[i]-r*i的递减队列呢?因为革更新答案时 是用h[i]+r*i + h[q[front]] - r*q[front]   . 代码: #

【动态规划】【单调队列】tyvj1305 最大子序和

http://blog.csdn.net/oiljt12138/article/details/51174560 单调队列优化dp #include<cstdio> #include<deque> #include<algorithm> #include<iostream> using namespace std; typedef long long ll; int n,m; ll a[300100],ans; deque<int>q; int

hdu_5884_Sort(二分+单调队列)

题目链接:hdu_5884_Sort 题意: 有n个数,每个数有个值,现在你可以选择每次K个数合并,合并的消耗为这K个数的权值和,问在合并为只有1个数的时候,总消耗不超过T的情况下,最小的K是多少 题解: 首先要选满足条件的最小K,肯定会想到二分. 然后是如何来写这个check函数的问题 我们要贪心做到使消耗最小,首先我们将所有的数排序 然后对于每次的check的mid都取最小的mid个数来合并,然后把新产生的数扔进优先队列,直到最后只剩一个数. 不过这样的做法是n*(logn)2 ,常数写的小

[Vijos 1243]生产产品(单调队列优化Dp)

Description 在经过一段时间的经营后,dd_engi的OI商店不满足于从别的供货商那里购买产品放上货架,而要开始自己生产产品了!产品的生产需要M个步骤,每一个步骤都可以在N台机器中的任何一台完成,但生产的步骤必须严格按顺序执行.由于这N台机器的性能不同,它们完成每一个步骤的所需时间也不同.机器i完成第j个步骤的时间为T[i,j].把半成品从一台机器上搬到另一台机器上也需要一定的时间K.同时,为了保证安全和产品的质量,每台机器最多只能连续完成产品的L个步骤.也就是说,如果有一台机器连续完

单调队列

先放上luogu的题目链接--滑稽窗口 然后我们再来讲单调队列 单调队列是指这样一种队列:在队列中的元素为单调递增状态或单调递减状态. 例如1 2 3 4 5和9 2 1都是单调队列,但1 2 2 3 4和4 3 4 5就不是单调队列. 但普通队列明显是维持不了单调队列的性质的. 为了维持单调队列的单调性质,我们只好想一些方法.方法就是修改队列的性质.单调队列不仅队头可以出队,队尾也可以出队. 比如说有一个单调队列是 1 3 7 8 现在突然要从队尾进来一个6如果单纯的把6插进队尾的话,那这个队

单调队列 BZOJ 2096 [Poi2010]Pilots

2096: [Poi2010]Pilots Time Limit: 30 Sec  Memory Limit: 162 MBSubmit: 819  Solved: 418[Submit][Status][Discuss] Description Tz又耍畸形了!!他要当飞行员,他拿到了一个飞行员测试难度序列,他设定了一个难度差的最大值,在序列中他想找到一个最长的子串,任意两个难度差不会超过他设定的最大值.耍畸形一个人是不行的,于是他找到了你. Input 输入:第一行两个有空格隔开的整数k(0

HDU 3706 Second My Problem First (单调队列)

题意:求给定的一个序列中最长子序列,该子序列的最大值和最小值介于m和k之间. 析:用两个单调队列来维护一个最小值,一个最大值,然后每次更新即可. 代码如下; #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include <string> #include <cstdlib> #include <cmath> #include <i

codevs3327选择数字(单调队列优化)

3327 选择数字 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题目描述 Description 给定一行n个非负整数a[1]..a[n].现在你可以选择其中若干个数,但不能有超过k个连续的数字被选择.你的任务是使得选出的数字的和最大. 输入描述 Input Description 第一行两个整数n,k 以下n行,每行一个整数表示a[i]. 输出描述 Output Description 输出一个值表示答案. 样例输入 Sample Input 5 2

【NOIP数据结构专项】单调队列单调栈

[洛谷P1901 ]发射站 http://www.luogu.org/problem/show?pid=1901 题目描述 某地有 N 个能量发射站排成一行,每个发射站 i 都有不相同的高度 Hi,并能向两边(当 然两端的只能向一边)同时发射能量值为 Vi 的能量,并且发出的能量只被两边最近的且比 它高的发射站接收. 显然,每个发射站发来的能量有可能被 0 或 1 或 2 个其他发射站所接受,特别是为了安 全,每个发射站接收到的能量总和是我们很关心的问题.由于数据很多,现只需要你帮忙计 算出接收