题目解析
注意到每个数的后继都是唯一的。
那么我们可以把这个序列挂到树上去,每个点的后继就是父节点。用类似于笛卡尔树的方式建树。
那么题目要求的最长贪心严格上升子序列的长度就是从儿子到父亲的最长链的长度。
题目有给定区间并修改,而如果区间中新出现一个数(a[x]),那么它子树内的所有结点的答案都会(+1),同样地,如果去掉这个数,它子树内的所有结点的答案都会(-1)。而一棵树又可以通过(dfs)序变成一个序列,所以可以用线段树维护区间加和区间最大值。
注意一点就是建树的时候要加个(n+1)上去,保证把所有的点都搞上树(相当于给个根,把所有最长贪心严格上升子序列的末尾的数并起来。
►Code View
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
#define N 1000005
#define MOD 998244353
#define INF 0x3f3f3f3f
int rd()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
return f*x;
}
struct node{
int mx,tag;
}tree[N<<2];
int n,k,a[N],stk[N],tp;
vector<int>G[N];
int dfn[N],tim,siz[N];
void dfs(int u)
{
dfn[u]=++tim;
siz[u]=1;
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i];
dfs(v);
siz[u]+=siz[v];
}
}
void PushDown(int i)
{
if(tree[i].tag)
{
tree[i<<1].mx+=tree[i].tag;
tree[i<<1].tag+=tree[i].tag;
tree[i<<1|1].mx+=tree[i].tag;
tree[i<<1|1].tag+=tree[i].tag;
tree[i].tag=0;
}
}
void PushUp(int i)
{
tree[i].mx=max(tree[i<<1].mx,tree[i<<1|1].mx);
}
void Update(int i,int l,int r,int ql,int qr,int val)
{
if(ql<=l&&r<=qr)
{
tree[i].mx+=val;
tree[i].tag+=val;
return ;
}
PushDown(i);
int mid=(l+r)>>1;
if(ql<=mid) Update(i<<1,l,mid,ql,qr,val);
if(qr>mid) Update(i<<1|1,mid+1,r,ql,qr,val);
PushUp(i);
}
int main()
{
n=rd(),k=rd();
for(int i=1;i<=n;i++)
a[i]=rd();
for(int i=1;i<=n;i++)
{//维护一个单调栈 找到每个数右边第一个比自己大的数
while(tp&&a[stk[tp]]<a[i])
{
G[i].push_back(stk[tp]);//i是stk[tp]的爸爸
tp--;
}
stk[++tp]=i;
}
while(tp)
{
G[n+1].push_back(stk[tp]);
tp--;
}
dfs(n+1);
for(int i=1;i<=k;i++)
Update(1,1,n+1,dfn[i],dfn[i]+siz[i]-1,1);
printf("%d ",tree[1].mx);
for(int i=k+1;i<=n;i++)
{
Update(1,1,n+1,dfn[i],dfn[i]+siz[i]-1,1);
Update(1,1,n+1,dfn[i-k],dfn[i-k]+siz[i-k]-1,-1);
printf("%d ",tree[1].mx);
}
return 0;
}