POI 2014 little bird

题目简介见https://www.luogu.org/problemnew/show/P3572

dp 方程应该很好写  (a 数组 为 树的高度)

j + k >= i  且 j < i

a[j] > a[i]   时 f[i] = min (f[i] , f[j]) ;

a[j] <= a[i] 时 f[i] = min (f[i] , f[j] +1 );

但是 复杂度 N^2*q  老爷机 受不了;

只能优化喽 ;

推断1 : 如果有 j > k 且  f[j] < f[k]  那么 j 永远比 k 优;

此时可以用单调队列; 执行推断1

但是 我们还要考虑树的高度;

推断2 :当单调队列中  j > k  且 f[j] == f[k]  且 a[j] > a[k]  那么j 比 k 更优秀 ;

终上所述 我们维护f非严格递增 , 当f相同时 , 维护a递减;

代码:

#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#include<cstring>
using namespace std;
#define M 1000100
#define ll long long
int n , m , f[M] , a[M] , maxn , high , k , q[M] , l , r;
void solve(){
    l = r = 1; q[1] = 1;
    memset(f , 0x3f , sizeof(f));
    f[1] = 0; 
    for (int i = 2 ; i <= n ; ++i){
        while (l <= r && q[l] + k < i ) ++l;
        if (a[q[l]] > a[i]) f[i] = f[q[l]];
        else f[i] = f[q[l]] + 1;
        while (f[i] < f[q[r]] && r >= l) --r;
        while (f[i] == f[q[r]]){
            if (a[i] >= a[q[r]]) --r;
            else break;
        } q[++r] = i;
    }
    printf ("%d\n" , f[n] );
}
int main(){
    freopen("c1.in" , "r" , stdin);
//    freopen("A.out" , "w" , stdout);
    scanf("%d" , &n);
    for (int i = 1 ; i <= n ; ++i) scanf("%d" , a + i);
    scanf("%d" , &m);
    while (m--){
        scanf("%d" , &k);
        solve();
    }
    return 0;
}

 

posted @ 2019-02-21 21:53  墨白——oier  阅读(108)  评论(0编辑  收藏  举报