codeforces1132G Greedy Subsequences

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yzyyylx/article/details/88222131

题面

题意

给出n个数和一个数m,求区间 [ 1 , m ] , [ 2 , m + 1 ] . . . . . . [ n m + 1 , n ] [1,m],[2,m+1]......[n-m+1,n] 的最长贪心子序列,最长贪心子序列的求法是,在每个数后面接上右边第一个比它大的数。

做法

这几个询问区间可以看作是在原区间的左边加数,右边删数。
如果我们记dp[i]表示以该位置为结尾的子序列的长度,则在删除左边数的贡献时,要把该点开始的子序列中的所有点的dp值-1,很麻烦。
而如果我们记dp[i]表示以该位置为开头的子序列的长度,则在加入右边的数 n u m [ k ] num[k] 的贡献时,要把左边所有满足右边比它大的第一个数是 n u m [ k ] num[k] 的点的dp值都+1,好像很麻烦,但是观察一下之后可以发现,这恰好是一段区间 [ v + 1 , k ] [v+1,k] ,v是k左边最大的且满足 n u m [ v ] > = n u m [ k ] num[v]>=num[k] 的数,这个只要用线段树维护区间加和区间查询最大值即可。

代码

#include<bits/stdc++.h>
#define N 1001000
using namespace std;

int n,m,tt,num[N],L[N];
struct Node
{
    int ls,rs,mx,sum;
}node[N<<1];

inline void up(int now)
{
    int L=node[now].ls,R=node[now].rs;
    node[now].mx=max(node[L].mx,node[R].mx)+node[now].sum;
}

void build(int now,int l,int r)
{
    if(l==r) return;
    int mid=((l+r)>>1);
    node[now].ls=++tt;
    build(tt,l,mid);
    node[now].rs=++tt;
    build(tt,mid+1,r);
}

void add(int now,int l,int r,int u,int v,int w)
{
    if(u<=l&&r<=v)
    {
	node[now].mx+=w;
	node[now].sum+=w;
	return;
    }
    int mid=((l+r)>>1);
    if(u<=mid) add(node[now].ls,l,mid,u,v,w);
    if(mid<v) add(node[now].rs,mid+1,r,u,v,w);
    up(now);
}

int ask(int now,int l,int r,int u,int v)
{
    if(u<=l&&r<=v) return node[now].mx;
    int res=0,mid=((l+r)>>1);
    if(u<=mid) res=max(res,ask(node[now].ls,l,mid,u,v));
    if(mid<v) res=max(res,ask(node[now].rs,mid+1,r,u,v));
    return res+node[now].sum;
}

int main()
{
    int i,j;
    cin>>n>>m;
    for(i=1;i<=n;i++) scanf("%d",&num[i]);
    num[0]=N;
    for(i=1;i<=n;i++)
    {
	L[i]=i-1;
	for(;num[i]>num[L[i]];L[i]=L[L[i]]);
    }
    build(tt=1,1,n);
    for(i=1;i<m;i++) add(1,1,n,L[i]+1,i,1);
    for(i=1,j=m;j<=n;i++,j++)
    {
	add(1,1,n,L[j]+1,j,1);
	printf("%d ",ask(1,1,n,i,j));
    }
}

猜你喜欢

转载自blog.csdn.net/yzyyylx/article/details/88222131