码迷,mamicode.com
首页 > 其他好文 > 详细

[POI 2014]PTA-Little Bird

时间:2020-01-28 17:40:19      阅读:56      评论:0      收藏:0      [点我收藏+]

标签:return   mes   solution   单调队列   limits   ref   code   否则   www   

Description

题库连接

给你 \(n\) 棵树,第 \(i\) 棵树的高度为 \(d_i\)。有一只鸟从 1 号树出发,每次飞跃不能超过 \(k\) 的距离。若飞到下一棵树的高度大于等于这一棵树那么耗费一点体力,否则不会。问你飞到第 \(n\) 棵树耗费最少体力为多少。多测,测试组数 \(q\)

\(1\leq n\leq 10^6,1\leq q\leq 25\)

Solution

朴素的 \(O(n^2)\) DP 是很容易想到的,记 \(f_i\) 表示飞到 \(i\) 上的最小花费。那么,\(f_i=\min\limits_{i-j\leq k} f_j+[d_i\geq d_j]\)

容易发现,最优的答案一定可以从所有满足条件的 \(j\)\(f_j\) 最小的地方转移过来,即 \(f_i\) 的值只可能是 \(\min\limits_{i-j\leq k} f_j\)\(\min\limits_{i-j\leq k} f_j+1\)

那么可以开一个单调队列维护满足条件的 \(j\),并且满足 \(f\) 单调;在 \(f\) 相同的同时 \(d\) 单调递减(从越高的树转移越优)。

Code

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+5;

int n, t, a[N], k, q[N], head, tail, f[N];

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &k); 
        q[head = tail = 1] = 1;
        for (int i = 2; i <= n; i++) {
            while (i-q[head] > k) ++head;
            f[i] = f[q[head]]+(a[q[head]] <= a[i]);
            while (head <= tail && (f[i] < f[q[tail]] || (f[i] == f[q[tail]] && a[i] >= a[q[tail]]))) --tail;
            q[++tail] = i;
        }
        printf("%d\n", f[n]);
    }
    return 0;
}

[POI 2014]PTA-Little Bird

标签:return   mes   solution   单调队列   limits   ref   code   否则   www   

原文地址:https://www.cnblogs.com/NaVi-Awson/p/12238386.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!