题目链接: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':' ');
}
}