【DFS序+线段树】CF1132G Greedy Subsequences

【题目】
CF
给定一个长度为 n n n的序列 a a a,问每个长度为 K K K的连续区间的最长贪心上升子序列有多长。
贪心上升子序列是指每个数找到它后面第一个比它大的数作为后继子序列。
n ≤ 1 0 6 n\leq 10^6 n106

【解题思路】
开始看错题了,以为就是区间最长上升子序列,然而正确的题目似乎性质更显然了。

我们按题意进行连边会形成一个森林,再新建一个节点作为超级根。

那么实际上对于每个询问的区间来说,对应了树上的一些节点,我们就是要求树在只看这些节点时节点最大深度(两关键点间的路径看作权 1 1 1)。

这个问题可以这样做:首先求出树的 DFS \text{DFS} DFS序,那么若一个节点有贡献对应的就是子树 + 1 +1 +1,询问就是全局的 max \text{max} max

反正最后复杂度就是 O ( n log ⁡ n ) O(n\log n) O(nlogn)了。

【参考代码】

#include
using namespace std;

const int N=1e6+10;

int n,K,ind,cnt;
int st[N],ed[N],a[N],b[N];
vector<int>G[N];

int read()
{
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}
void write(int x){if(x>9)write(x/10);putchar(x%10^48);}
void writesp(int x){write(x);putchar(' ');}

struct Segment
{
#define ls (x<<1)
#define rs (x<<1|1)
	int tag[N<<2],mx[N<<2];
	void pushdown(int x)
	{
		if(!tag[x]) return;
		int t=tag[x];tag[x]=0;
		tag[ls]+=t;tag[rs]+=t;mx[ls]+=t;mx[rs]+=t; 
	}
	void pushup(int x){mx[x]=max(mx[ls],mx[rs]);}
	void update(int x,int l,int r,int L,int R,int v)
	{
		if(L<=l && r<=R) {mx[x]+=v;tag[x]+=v;return;}
		pushdown(x);
		int mid=(l+r)>>1;
		if(L<=mid) update(ls,l,mid,L,R,v);
		if(R>mid) update(rs,mid+1,r,L,R,v);
		pushup(x);
	}
#undef ls
#undef rs
}T;

void add(int u,int v){G[u].push_back(v);}
void dfs(int x){st[x]=++ind; for(auto v:G[x]) dfs(v); ed[x]=ind;}

int main()
{
#ifdef Durant_Lee
	freopen("CF1132G.in","r",stdin);
	freopen("CF1132G.out","w",stdout);
#endif
	n=read();K=read();a[n+1]=n+1;
	for(int i=1;i<=n;++i) a[i]=read(),b[i]=i+1;
	for(int i=n;i;--i)
	{
		while(a[b[i]]<=a[i]) b[i]=b[b[i]];
		add(b[i],i);
	}
	++n;dfs(n);
	for(int i=1;i<=K;++i) T.update(1,1,n,st[i],ed[i],1); writesp(T.mx[1]);
	for(int i=K+1;i<n;++i) T.update(1,1,n,st[i-K],ed[i-K],-1),T.update(1,1,n,st[i],ed[i],1),writesp(T.mx[1]);
	return 0;
}

你可能感兴趣的:(数据结构-线段树)