[BZOJ4491]我也不知道题目名字是什么

bzoj

题意

给定一个序列A[i],每次询问l,r,求[l,r]内最长子串,使得该子串为不上升子串或不下降子串。

sol

这还不带修改?
话说回来这种东西维护起来也很简单。记一下每块左右边的数字,最长的不下降子串长度,左连续、右连续的最长不下降子串长度,还有总长度。向上合并只要分类讨论一下即可。
用一个struct然后重载一下加法写起来是真的爽。

什么?你说还有不上升的?开两棵线段树,另一棵取反就可以了。

code

#include<cstdio>
#include<algorithm>
using namespace std;
int gi()
{
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 5e4+5;
int n,m,a[N],maxa;
struct Data{int lc,rc,vl,vr,v,len;};
Data operator + (Data a,Data b)
{
	Data c;
	c.v=max(a.v,b.v);c.vl=a.vl;c.vr=b.vr;
	if (a.rc<=b.lc)
	{
		c.v=max(c.v,a.vr+b.vl);
		if (a.v==a.len) c.vl=a.len+b.vl;
		if (b.v==b.len) c.vr=b.len+a.vr;
	}
	c.lc=a.lc;c.rc=b.rc;c.len=a.len+b.len;
	return c;
}
struct segment_tree{
	Data t[N<<2];
	void build(int x,int l,int r)
		{
			if (l==r) {t[x]=(Data){a[l],a[l],1,1,1,1};return;}
			int mid=l+r>>1;
			build(x<<1,l,mid);build(x<<1|1,mid+1,r);
			t[x]=t[x<<1]+t[x<<1|1];
		}
	Data query(int x,int l,int r,int ql,int qr)
		{
			if (l>=ql&&r<=qr) return t[x];
			int mid=l+r>>1;
			if (qr<=mid) return query(x<<1,l,mid,ql,qr);
			if (ql>mid) return query(x<<1|1,mid+1,r,ql,qr);
			return query(x<<1,l,mid,ql,qr)+query(x<<1|1,mid+1,r,ql,qr);
		}
}A,B;
int main()
{
	n=gi();
	for (int i=1;i<=n;++i) a[i]=gi(),maxa=max(maxa,a[i]+1);
	A.build(1,1,n);
	for (int i=1;i<=n;++i) a[i]=maxa-a[i];
	B.build(1,1,n);
	m=gi();
	while (m--)
	{
		int l=gi(),r=gi();
		printf("%d
",max(A.query(1,1,n,l,r).v,B.query(1,1,n,l,r).v));
	}
	return 0;
}
原文地址:https://www.cnblogs.com/zhoushuyu/p/8710952.html