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

#include<bits/stdc++.h>
using namespace std;
int st[1000007];
int top;
int s[1000007],t[1000007];
int mx[4000007];
int sum[4000007];
int head[1000007],to[2000007],nex[2000007];
int n,k;
int a[10000077];
int dfn;
int tot;
void pushup(int rt){
    mx[rt]=max(mx[rt<<1],mx[rt<<1|1]);
}
void pushdown(int rt){
    if(sum[rt]){
        sum[rt<<1]+=sum[rt];
        sum[rt<<1|1]+=sum[rt];
        mx[rt<<1]+=sum[rt];
        mx[rt<<1|1]+=sum[rt];
        sum[rt]=0;
    }
}
void change(int rt,int l,int r,int L,int R,int x){//线段树区间更新
    if(L<=l&&r<=R){
        sum[rt]+=x;
        mx[rt]+=x;
        return ;
    }
    pushdown(rt);
    int mid=(l+r)>>1;
    if(L<=mid)
        change(rt<<1,l,mid,L,R,x);
    if(R>mid)
        change(rt<<1|1,mid+1,r,L,R,x);
    pushup(rt);
}
void add(int x,int y){//链式前向星,连边
    nex[++tot]=head[x];//上一条边
    head[x]=tot;//最后一条边
    to[tot]=y;//连向的点
}
void dfs(int x){
    s[x]=++dfn;//这个点子树深度的最小值
    for(int i=head[x];i;i=nex[i])
        dfs(to[i]);
    t[x]=dfn;//这个点子树深度的最大值
}
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        while(top&&a[st[top]]<a[i]){//单调栈
            add(i,st[top]);//连边,前一个是后一个离得最近的比它大的数的位置
            top--;
        }
        st[++top]=i;//进入栈顶
    }
    while(top){
        add(n+1,st[top]);//顶端的数字给一个根节点将它们建成树
        top--;
    }
    dfs(n+1);//从根节点遍历
    for(int i=1;i<=k;i++)
        change(1,1,n+1,s[i],t[i],1);
    printf("%d ",mx[1]);
    for(int i=k+1;i<=n;i++){
        change(1,1,n+1,s[i],t[i],1);//当区间右端点右移时,新加入区间的这个数会对原区间中比这个数小的数的答案+1,也就是将这个数在树上的子树中所有点的答案+1(这些答案+1的所有点中虽然包括区间之前的数但显然这些数的答案不会比区间内数的答案更大,最多只会与最大值相同)
        change(1,1,n+1,s[i-k],t[i-k],-1);//同样当区间左端点右移时,就将这个数在树上的子树中所有点的答案−1来确保区间之前的数的答案不会比区间中数的答案更优
        printf("%d ",mx[1]);
    }
}

猜你喜欢

转载自www.cnblogs.com/ldudxy/p/10611902.html
今日推荐