Educational Codeforces Round 61 (Rated for Div. 2) G. Greedy Subsequences 单调栈+线段树

题目链接:http://codeforces.com/contest/1132/problem/G

 

题意:

      给你一个长度为n的数组a,现在给你一个定义,在一个贪心的子序列A中,假设A[i]与A[i+1]在原数组中的下标为x,y的不仅要满足a[x]<a[y],在原数组中的位置中y也要最小,即y为第一个权值大于x的下标。

      现在给你一个k,问你[1,k],[2,k+1]....[n-k+1,n]这n-k+1个区间中的最长贪心子序列的长度。

 

做法:

       如果用一般的dp去写,用dp[i]表示以该位置为结尾的子序列的最长长度,那么在遍历中想要消除当前最左边数字的贡献时,要把该点影响到的dp的值都减一,显然不好做。
       而换一个思路,记dp[i]表示以该位置为开头的子序列的长度,则在加入下一个数a[x]的贡献时,要把左边第一个大于等于它的数到它自己的dp值都加上一,这恰好是一段区间,令pre是x左边最大的且满足num[pre]>=num[x]的下标,那么这个区间就是[pre+1,x],然后就是一个线段树了。


#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
const int maxn=1000005;
int a[maxn],add[maxn],x,premo[maxn],n,k;
struct node{
    int val,id;
    node(){}
    node(int val,int id):val(val),id(id){}
};
stack<node> S;
int mma[maxn<<2],laz[maxn<<2];
void pushdown(int rt){
    if(laz[rt]){
        laz[lson]+=laz[rt];
        laz[rson]+=laz[rt];
        mma[lson]+=laz[rt];
        mma[rson]+=laz[rt];
        laz[rt]=0;
    }
}
void update(int ql,int qr,int l,int r,int rt){
    if(ql<=l&&r<=qr){
        laz[rt]+=1;
        mma[rt]+=1;
        return ;
    }
    int mid=l+r>>1;
    pushdown(rt);
    if(ql<=mid) update(ql,qr,l,mid,lson);
    if(qr>mid) update(ql,qr,mid+1,r,rson);
    mma[rt]=max(mma[lson],mma[rson]);

}
int query(int ql,int qr,int l,int r,int rt){
    if(ql<=l&&r<=qr){
        return mma[rt];
    }
    int mid=l+r>>1;
    int ans=0;
    pushdown(rt);
    if(ql<=mid) ans=max(ans,query(ql,qr,l,mid,lson));
    if(qr>mid) ans=max(ans,query(ql,qr,mid+1,r,rson));
    return ans;
}
int main(){
    scanf("%d%d",&n,&k);
    S.push(node(1e8,0));
    for(int i=1;i<=n;i++){
        scanf("%d",&x);
        while(S.top().val<x) S.pop();
        if(S.top().val==x){
           premo[i]=S.top().id;
           S.pop();
        }
        else premo[i]=S.top().id;
        S.push(node(x,i));
    }
    for(int i=1;i<k;i++){
        int pre=premo[i]+1;
        update(pre,i,1,n,1);
    }
    for(int i=1,j=k;j<=n;i++,j++){
        int pre=premo[j]+1;
        update(pre,j,1,n,1);
        printf("%d%c",query(i,j,1,n,1),j==n?'\n':' ');
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值