Codeforces Round #727 (Div. 2) F. Strange Array(思维,线段树子段和)
Posted issue是fw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #727 (Div. 2) F. Strange Array(思维,线段树子段和)相关的知识,希望对你有一定的参考价值。
题意
给定长度为 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| ∣k−f∣
求出最大值
考虑只查询位置 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+w−1]抹掉, [ r − w + 1 , r ] [r-w+1,r] [r−w+1,r]抹掉
剩下 [ l + w , r − w ] [l+w,r-w] [l+w,r−w]有 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)/2−1=(1+(f+1))/2−1=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)