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;
}

 

posted @ 2015-06-06 22:39  moonbay  阅读(256)  评论(0编辑  收藏  举报