C sequence 2019牛客暑期多校训练营(第四场)

https://ac.nowcoder.com/acm/contest/884/C

听说这题是笛卡尔树,发现去年这题是队友写的,来补一补

然而完全不想用笛卡尔树写,单调栈多爽

对于a[i],找出他为最小的l[i]和r[i],然后如果a[i]<0,就跨过i的找前缀和相减最小值,否则则找区间和最大值

辣鸡题目卡st表要写线段树

#include
using namespace std;
typedef long long ll;

const int maxl=3e6+10;

int n,k;ll ans;
int a[maxl],b[maxl],s[maxl],l[maxl],r[maxl];
ll sum[maxl];
struct node
{
	int l,r;ll mi,mx;
}tr[maxl<<2];

inline void build(int k,int l,int r)
{
	tr[k].l=l;tr[k].r=r;
	if(l==r)
	{
		tr[k].mi=tr[k].mx=sum[l];
		return;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	tr[k].mi=min(tr[k<<1].mi,tr[k<<1|1].mi);
	tr[k].mx=max(tr[k<<1].mx,tr[k<<1|1].mx);
}

inline void prework()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&b[i]);
		sum[i]=sum[i-1]+b[i];
	}
	build(1,0,n);
}

inline ll getmx(int k,int l,int r)
{
	if(tr[k].l==l && tr[k].r==r)
		return tr[k].mx;
	int mid=(tr[k].l+tr[k].r)>>1;
	if(r<=mid)
		return getmx(k<<1,l,r);
	else if(l>mid)
		return getmx(k<<1|1,l,r);
	else 
		return max(getmx(k<<1,l,mid),getmx(k<<1|1,mid+1,r));
}

inline ll getmi(int k,int l,int r)
{
	if(tr[k].l==l && tr[k].r==r)
		return tr[k].mi;
	int mid=(tr[k].l+tr[k].r)>>1;
	if(r<=mid)
		return getmi(k<<1,l,r);
	else if(l>mid)
		return getmi(k<<1|1,l,r);
	else
		return min(getmi(k<<1,l,mid),getmi(k<<1|1,mid+1,r));
}

inline void mainwork()
{
	int top=0;s[0]=0;
	for(int i=1;i<=n;i++)
	{
		while(top>0 && a[i]<=a[s[top]])
			top--;
		s[++top]=i;l[i]=s[top-1]+1;
	}
	top=0;s[0]=n+1;
	for(int i=n;i>=1;i--)
	{
		while(top>0 && a[i]<=a[s[top]])
			top--;
		s[++top]=i;r[i]=s[top-1]-1;
	}
	ll rs,ls;ans=0;
	for(int i=1;i<=n;i++)
	if(a[i]<0)
	{
		ls=getmx(1,l[i]-1,i-1);
		rs=getmi(1,i,r[i]);
		ans=max(ans,(rs-ls)*a[i]);
	}
	else
	{
		ls=getmi(1,l[i]-1,i-1);
		rs=getmx(1,i,r[i]);
		ans=max(ans,(rs-ls)*a[i]);
	}
}

inline void print()
{
	printf("%lld\n",ans);
}

int main()
{
	prework();
	mainwork();
	print();
	return 0;
}

 

你可能感兴趣的:(单调栈)