codeforces1132G Greedy Subsequences

題面

題意

給出n個數和一個數m,求區間[1,m],[2,m+1]......[nm+1,n][1,m],[2,m+1]......[n-m+1,n]的最長貪心子序列,最長貪心子序列的求法是,在每個數後面接上右邊第一個比它大的數。

做法

這幾個詢問區間可以看作是在原區間的左邊加數,右邊刪數。
如果我們記dp[i]表示以該位置爲結尾的子序列的長度,則在刪除左邊數的貢獻時,要把該點開始的子序列中的所有點的dp值-1,很麻煩。
而如果我們記dp[i]表示以該位置爲開頭的子序列的長度,則在加入右邊的數num[k]num[k]的貢獻時,要把左邊所有滿足右邊比它大的第一個數是num[k]num[k]的點的dp值都+1,好像很麻煩,但是觀察一下之後可以發現,這恰好是一段區間[v+1,k][v+1,k],v是k左邊最大的且滿足num[v]>=num[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));
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章