牛客 - sequence(笛卡尔树+线段树)

题目链接:点击查看

题目大意:给出一个长度为 n 的数列 a 和数列 b ,求max_{1<=l<=r<=n}(min(a_{l...r})*sum(b_{l...r}))

题目分析:不算难的题目,对于每个 a[ i ] 求一下贡献然后维护最大值就好,具体思路就是,先找出每个 a[ i ] 左右两侧分别小于 a[ i ] 的位置,分别记为 l 和 r,再对数列 b 维护一下前缀和 sum,比较显然的就是 ( l , r ) 这段区间内,凡是跨过位置 i 的区间最小值一定是 a[ i ],然后分类讨论一下:

  1. 如果 a[ i ] > 0:在 [ l - 1 , i - 1 ] 中找到 sum 的最小值,在 [ i , r ] 中找到 sum 的最大值,贡献为 a[ i ] * ( mmax - mmin )
  2. 如果 a[ i ] < 0:在 [ l - 1 , i - 1 ] 中找到 sum 的最大值,在 [ i , r ] 中找到 sum 的最小值,贡献为 a[ i ] * ( mmin - mmax )

区间最大值可以用 st 表或者线段树来维护,现在的问题就是如何快速找到每个 a[ i ] 的 l 和 r,用单调栈 O( n ) 扫一遍显然是没问题的,但为了练习笛卡尔树,可以利用笛卡尔树的性质,O( n ) 无脑建树后,dfs 扫一遍每个节点计算贡献也是一样的

代码:
 

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
 
typedef long long LL;
 
typedef unsigned long long ull;
 
const LL inf=0x3f3f3f3f3f3f3f3f;
 
const int N=3e6+100;

LL sum[N],ans;

struct Node1
{
	int l,r;
	LL mmin,mmax;
}tree[N<<2];

void pushup(int k)
{
	tree[k].mmax=max(tree[k<<1].mmax,tree[k<<1|1].mmax);
	tree[k].mmin=min(tree[k<<1].mmin,tree[k<<1|1].mmin);
}

void build(int k,int l,int r)
{
	tree[k].l=l;
	tree[k].r=r;
	if(l==r)
	{
		tree[k].mmax=tree[k].mmin=sum[l];
		return;
	}
	int mid=l+r>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	pushup(k);
}

LL query_min(int k,int l,int r)
{
	if(tree[k].rr)
		return inf;
	if(tree[k].l>=l&&tree[k].r<=r)
		return tree[k].mmin;
	return min(query_min(k<<1,l,r),query_min(k<<1|1,l,r));
}

LL query_max(int k,int l,int r)
{
	if(tree[k].rr)
		return -inf;
	if(tree[k].l>=l&&tree[k].r<=r)
		return tree[k].mmax;
	return max(query_max(k<<1,l,r),query_max(k<<1|1,l,r));
}

struct Node2
{
	int l,r,val;
}t[N];

stacks;

void insert(int x)
{
	while(s.size()&&t[s.top()].val>t[x].val)
		s.pop();
	t[x].l=t[s.top()].r;//x->lson
	t[s.top()].r=x;//fa->x(rson)
	s.push(x);
}

void dfs(int k,int l,int r)
{
	if(t[k].val>0)
	{
		LL mmin=query_min(1,l-1,k-1);
		LL mmax=query_max(1,k,r);
		ans=max(ans,t[k].val*(mmax-mmin));
	}
	else
	{
		LL mmin=query_min(1,k,r);
		LL mmax=query_max(1,l-1,k-1);
		ans=max(ans,t[k].val*(mmin-mmax));
	}
	if(t[k].l)
		dfs(t[k].l,l,k-1);
	if(t[k].r)
		dfs(t[k].r,k+1,r);
}

void init()
{
	t[0].val=-0x3f3f3f3f;
	t[0].l=t[0].r=0;
	s.push(0);
}

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	int n;
	scanf("%d",&n);
	init();
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&t[i].val);
		insert(i);
	}
	for(int i=1;i<=n;i++)
	{
		int num;
		scanf("%d",&num);
		sum[i]=sum[i-1]+num;
	}
	build(1,0,n);
	ans=-inf;
	dfs(0,1,n);
	printf("%lld\n",ans);













   return 0;
}

 

你可能感兴趣的:(线段树,数据结构,单调栈/单调队列)