CF1132G

听说,一个好的Oier都是题目喂出来的。

题目

定义一个序列的最长贪心严格上升子序列为:若选出的子序列为 \(a\),对于其中相邻两项 \(i,j\),不存在 b\(i<k<j\),满足在原序列 \(b\) 中,有 \(b_i<b_k\),换句话说就是选择一个元素后必须选择它之后第一个大于它的元素

给定一个长度为 \(n\) 的序列,同时给定一个常数 \(k\),求该序列的所有长度为 \(k\)的子区间的最长贪心严格上升子序列的长度

数据范围\(10^6\)

解题思路

先想了一个长链剖分的假做法,发现不会处理长儿子,自闭了。

考虑每个点的出度都不超过1,所以他构成了一颗森林

\(f_x\)表示从x开始往上走,最长走多远。

每加入一个点,需要把它的子树内的所有点权值+1

每删除一个点,需要把它的权值变得足够小

线段树维护即可

代码

#include<bits/stdc++.h>
#define now edge[i].v
#define go(x) for(int i=head[x];i;i=edge[i].nxt)
#define ls o<<1,l,mid
#define rs o<<1|1,mid+1,r
using namespace std;
const int sz=1e6+527;
int n,k;
int cnt,T;
int x,y,z;
int head[sz];
int a[sz],ans[sz];
int dfn[sz],lev[sz];
stack<int>s;
struct Edge{
    int v,nxt;
}edge[sz];
struct node{
    int tag,mx;
}tr[sz<<2];
void add(int u,int v){
    edge[++cnt]=(Edge){v,head[u]};
    head[u]=cnt;
}
void update(int o){
    tr[o<<1].tag+=tr[o].tag;
    tr[o<<1|1].tag+=tr[o].tag;
    tr[o<<1].mx+=tr[o].tag;
    tr[o<<1|1].mx+=tr[o].tag;
    tr[o].tag=0;
}
void modify(int o,int l,int r){
    if(x<=l&&r<=y) return (void)(tr[o].mx+=z,tr[o].tag+=z);
    if(tr[o].tag) update(o);
    int mid=(l+r)>>1;
    if(x<=mid) modify(ls);
    if(y>mid) modify(rs);
    tr[o].mx=max(tr[o<<1].mx,tr[o<<1|1].mx);
}
void dfs(int x){
    dfn[x]=++T;
    go(x)
        dfs(now);
    lev[x]=T;
}
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        while(!s.empty()&&a[s.top()]<a[i]){
            add(i,s.top());
            s.pop();
        }
        s.push(i);
    }
    n++;
    while(!s.empty()){
        add(n,s.top());
        s.pop();
    }
    dfs(n);
    for(int i=1;i<=k;i++){
        x=dfn[i],y=lev[i],z=1;
        modify(1,1,n);
    }
    ans[1]=tr[1].mx;
    for(int i=k+1;i<n;i++){
        x=dfn[i],y=lev[i],z=1;
        modify(1,1,n);
        x=dfn[i-k],y=lev[i-k],z=-1;
        modify(1,1,n);
        ans[i-k+1]=tr[1].mx;
    }
    for(int i=1;i<=n-k;i++) printf("%d ",ans[i]);
}

题目链接

转载于:https://www.cnblogs.com/river-flows-in-you/p/11300170.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值