CF #278 div2 D Strip 线段树+优先队列

题意:  有N个连续的数,求一种划分,使得满足以下条件: 1,每一个块中最少有L个数。2,块中最大数和最小数的差不超过S。求最少要划分成多少块,如果没有满足条件的划分,输出-1;

思路:  dp[i]表示前i个字符中满足条件的最小划分。则dp[i]=min(dp[0],dp[1],dp[i-L]),这是第一个条件,我们可以用优先队列简化这个过程。第二个条件需要求在dp[k]+1的位置到dp[i]的位置(k为转移点)这一段中的最大值和最小值的差,这一个过程可以用线段树简化。


/*I have given up the treatment-_-||*/
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<climits>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<vector>
using namespace std;
#define mod 1000000007
typedef long long LL;
const int maxn=200010;
const int maxm=110;
const int inf=1e9;
int n,m;
LL a[maxn];
struct Node
{
    int l;
    int r;
    LL w;
};
Node Tmax[maxn<<2];
Node Tmin[maxn<<2];
struct Dp
{
    LL val;
    int pos;
    bool operator < (const Dp &tt) const {
        return val>tt.val;   //val小的在前面
    }
}dp[maxn];
void Buildtree(int left,int right,int rt)
{
    Tmax[rt].l=Tmin[rt].l=left;
    Tmax[rt].r=Tmin[rt].r=right;
    if(left==right){
        Tmax[rt].w=Tmin[rt].w=a[left];
    }
    else{
        int mid=(left+right)>>1;
        Buildtree(left,mid,rt<<1);
        Buildtree(mid+1,right,(rt<<1)+1);
        Tmax[rt].w=max(Tmax[rt<<1].w,Tmax[(rt<<1)+1].w);
        Tmin[rt].w=min(Tmin[rt<<1].w,Tmin[(rt<<1)+1].w);
     }
}
LL querymax(int left,int right,int rt)
{
    LL temp;
    if(Tmax[rt].l>=left&&Tmax[rt].r<=right){
        return Tmax[rt].w;
    }
    else{
        temp=-inf;
        if(Tmax[rt<<1].r>=left){
            temp=max(temp,querymax(left,right,rt<<1));
        }
        if(Tmax[(rt<<1)+1].l<=right){
            temp=max(temp,querymax(left,right,(rt<<1)+1));
        }
    }
    return temp;
}
LL querymin(int left,int right,int rt)
{
    LL temp;
    if(Tmin[rt].l>=left&&Tmin[rt].r<=right){
        return Tmin[rt].w;
    }
    else{
        temp=inf;
        if(Tmin[rt<<1].r>=left){
            temp=min(temp,querymin(left,right,rt<<1));
        }
        if(Tmin[(rt<<1)+1].l<=right){
            temp=min(temp,querymin(left,right,(rt<<1)+1));
        }
    }
    return temp;
}
int main()
{
#ifndef ONLINE_JUDGE
    //freopen ("in.txt" , "r" , stdin);
    //freopen ("output.txt" , "w" , stdout);
#endif
    int i,j;
    int s,l;
    scanf("%d%d%d",&n,&s,&l);
    for(i=1;i<=n;i++){
        scanf("%I64d",&a[i]);
    }
    Buildtree(1,n,1);
    dp[0].val=0;
    dp[0].pos=0;
    priority_queue<Dp> que;
    for(i=1;i<=n;i++){
        int temp=i-l;
        Dp tp;
        if(temp>=0&&dp[temp].val>=0)
            que.push(dp[temp]);
        while(!que.empty()){
            tp=que.top();
            int pp=tp.pos;
            LL tx=querymax(pp+1,i,1);
            LL tn=querymin(pp+1,i,1);
            if(abs(tx-a[i])>s||abs(tn-a[i])>s||abs(tx-tn)>s){
                que.pop();
                continue;
            }
            else break;
        }
        if(que.empty()){
            dp[i].val=-1;
            dp[i].pos=i;
            continue;
        }
        else{
            int pp=tp.pos;
            dp[i].val=tp.val+1;
            dp[i].pos=i;
        }
    }
    printf("%I64d\n",dp[n].val);
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值