Codeforces Round #727 (Div. 2) F. Strange Array(思维,线段树子段和)

Posted issue是fw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #727 (Div. 2) F. Strange Array(思维,线段树子段和)相关的知识,希望对你有一定的参考价值。

LINK

题意

给定长度为 n n n的数组 a a a,对于每个 a i a_i ai

选定一个 l < = i < = r l<=i<=r l<=i<=r,把区间 [ l , r ] [l,r] [l,r]排序,中位数是第 ( l + r + 1 ) / 2 (l+r+1)/2 (l+r+1)/2个数记作 k k k

再记 a i a_i ai在排序后的数字中是第 f f f个位置,那么代价是 ∣ k − f ∣ |k-f| kf

求出最大值


考虑只查询位置 i i i的答案怎么做,若选定区间 [ l , r ] [l,r] [l,r],那么 a i a_i ai可能在中位数左边,也可能在右边

如果在左边,考虑把小于 a i a_i ai的数权值看作 − 1 -1 1,大于等于 a i a_i ai的数权值为 1 1 1

那么相当于求一个以 a i a_i ai结尾的最大子段和,一个以 a i a_i ai开头的最大子段和

中和一下值为 f f f显然 f f f越大答案越大

考虑选的区间 [ l , r ] [l,r] [l,r]排序后 a l   a l + 1 . . . . a i . . . . a r a_l\\ a_{l+1}....a_i....a_r al al+1....ai....ar

− 1 -1 1 1 1 1抵消了 w w w次,相当于前面 [ l , l + w − 1 ] [l,l+w-1] [l,l+w1]抹掉, [ r − w + 1 , r ] [r-w+1,r] [rw+1,r]抹掉

剩下 [ l + w , r − w ] [l+w,r-w] [l+w,rw] f f f个数字,且 l + w = i l+w=i l+w=i

那么此时答案就是 ( l + r + 1 ) / 2 − 1 = ( 1 + ( f + 1 ) ) / 2 − 1 = f / 2 (l+r+1)/2-1=(1+(f+1))/2-1=f/2 (l+r+1)/21=(1+(f+1))/21=f/2

而线段树是可以维护区间子段和的,这个没问题

但是对于每个值 a i a_i ai来说,需要的线段树都是不同的啊!!

没关系,把 a i a_i ai的值从小到大计算,这样每次只需要修改部分值即可

然后考虑每个 a i a_i ai在中位数的右边,再做一遍,对答案取 m a x max max即可

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5+20;
#define mid (l+r>>1)
#define ls (rt<<1)
#define rs (rt<<1|1)
#define lson ls,l,mid
#define rson rs,mid+1,r
int n,a[maxn],ans[maxn];
int lmx[maxn<<2],rmx[maxn<<2],sum[maxn<<2]; 
typedef pair<int,int>p;
vector<int>vec[maxn];
void push_up(int rt,int l,int r)
{
	sum[rt] = sum[ls]+sum[rs];
	lmx[rt] = max( lmx[ls],sum[ls]+lmx[rs] );
	rmx[rt] = max( rmx[rs],sum[rs]+rmx[ls] );	
}
void build(int rt,int l,int r)
{
	if( l==r ){ lmx[rt] = rmx[rt] = sum[rt] = 1; return; }
	build( lson ); build( rson );
	push_up( rt,l,r );
}
void update(int rt,int l,int r,int pos,int val)
{
	if( l>pos || r<pos )	return;
	if( l==r && l==pos ){ lmx[rt] = rmx[rt] = sum[rt] = val; return; }
	update( lson,pos,val ); update( rson,pos,val );
	push_up(rt,l,r);
}
p ask1(int rt,int l,int r,int L,int R)
{
	if( l>=L && r<=R )	return p( lmx[rt],sum[rt] );
	if( R<=mid )	return ask1(lson,L,R);
	else if( L>mid )	return ask1(rson,L,R);
	else
	{
		p q1 = ask1( lson,L,R ), q2 = ask1( rson,L,R );
		return p( max( q1.first,q1.second+q2.first ),q1.second+q2.second );
	}
}
p ask2(int rt,int l,int r,int L,int R)
{
	if( l>=L && r<=R )	return p( rmx[rt],sum[rt] );
	if( R<=mid )	return ask2(lson,L,R);
	else if( L>mid )	return ask2(rson,L,R);
	else
	{
		p q1 = ask2( lson,L,R ), q2 = ask2( rson,L,R );
		return p( max( q2.first,q2.second+q1.first ),q1.second+q2.second );
	}
}
void solve1()
{
	build( 1,1,n );
	for(int i=1;i<=n;i++)
	{
		//小于i的都变成负数,求最大子段和
		for(auto v:vec[i] )
		{
			int temp = 0;
			if( v+1<=n )
				temp = max(0,ask1(1,1,n,v+1,n ).first );
			temp += max(0,ask2(1,1,n,1,v).first );
			ans[v] = temp/2;
		}
		for(auto v:vec[i] )	update(1,1,n,v,-1);
	}
}
void solve2()//大于i的都变成负数,求最大子段和 
{
	build(1,1,n);
	for(int i=n;i>=1;i--)
	{
		for(auto v:vec[i] )
		{
			int temp = 0;
			if( v+1<=n )
				temp = max(0,ask1(1,1,n,v+1,n ).first );
			temp += max(0,ask2(1,1,n,1,v).first );
			ans[v] = max( ans[v],temp-(temp+2)/2 );		
		}
		for(auto v:vec[i] )	update(1,1,n,v,-1);
	}
}
int main()
{ 
	cin >> n;
	for(int i=1;i<=n;i++)
	{
		int x;	scanf("%d",&x);
		vec[x].push_back( i );
	}
	solve1(); solve2();
	for(int i=1;i<=n;i++)	printf("%d ",ans[i] );
}

以上是关于Codeforces Round #727 (Div. 2) F. Strange Array(思维,线段树子段和)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #727 div.2 A-F题解

Codeforces Round #727 (Div. 2) F. Strange Array(思维,线段树子段和)

Codeforces Round #727 (Div. 2) E. Game with Cards(巧妙dp的优化)

Codeforces Round #727 (Div. 2) E. Game with Cards(dp优化,从n^2到nlog到n)

Codeforces Round #436 E. Fire(背包dp+输出路径)

[ACM]Codeforces Round #534 (Div. 2)