[CF1539F]Strange Array

Strange Array

题解

考虑我们如何求出一个数与这个序列中间数之间的距离。
假设这个序列中有 x x x个数比 a i a_{i} ai大,有 y y y个数与 a i a_{i} ai一样,有 z z z个数比 a i a_{i} ai小。
a i a_{i} ai不小于这个序列的中间数时,它们之间的距离为 y + z − ⌈ x + y + z 2 ⌉ = ⌊ y + z − x 2 ⌋ y+z-\left\lceil\frac{x+y+z}{2}\right\rceil=\left\lfloor\frac{y+z-x}{2}\right\rfloor y+z2x+y+z=2y+zx
a i a_{i} ai不大于这个序列的中间数时,它们之间的距离为 x + y − ⌊ x + y + z 2 ⌋ = ⌈ x + y − z 2 ⌉ x+y-\left\lfloor\frac{x+y+z}{2}\right\rfloor=\left\lceil\frac{x+y-z}{2}\right\rceil x+y2x+y+z=2x+yz
我们可以分大于中间数与小于中间数两种情况去求答案。

对于 a i a_{i} ai不小于中间数的答案,我们先将所有数都赋值为 − 1 -1 1,再从 i = 1 i=1 i=1开始,将等于 i i i的数的值赋值为 1 1 1
那么对于 a j = i a_{j}=i aj=i,它的 y + z − x y+z-x y+zx相当于是最大的一段区间和。
我们求最大的区间和可以利用线段树来维护每个位置的前缀,记点 i i i的前缀为 p r e i pre_{i} prei,有
( y + z − x ) m a x = max ⁡ k = j n p r e k − min ⁡ k = 0 j − 1 p r e k (y+z-x)_{max}=\max_{k=j}^{n}pre_{k}-\min_{k=0}^{j-1}pre_{k} (y+zx)max=k=jmaxnprekk=0minj1prek
我们可以通过线段树的区间修改来维护前缀,通过线段树的区间最值来求出它的最大/小前缀。
对于 a i a_{i} ai不大于中间数的答案,做法是相同的,只需要从 i = n i=n i=n开始即可。

时间复杂度 O ( n l o g   n ) O\left(nlog\,n\right) O(nlogn)

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 200005
#define lowbit(x) (x&-x)
#define reg register
#define mkpr make_pair
typedef long long LL;
typedef unsigned long long uLL;
const int INF=0x3f3f3f3f;
const int mo=1e9+7;
const int iv2=5e8+4;
const int lim=1000000;
const int jzm=2333;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y){return x+y<mo?x+y:x+y-mo;}
int n,a[MAXN],ans[MAXN];
vector<int>vec[MAXN];
class SegmentTree{
	public:
		int trmx[MAXN<<2],trmn[MAXN<<2],lzy[MAXN<<2];
		void pushup(int rt){
			trmx[rt]=max(trmx[rt<<1],trmx[rt<<1|1]);
			trmn[rt]=min(trmn[rt<<1],trmn[rt<<1|1]);
		}
		void pushdown(int rt,int l,int r){
			if(lzy[rt]){
				int mid=l+r>>1;
				trmx[rt<<1]+=lzy[rt];trmx[rt<<1|1]+=lzy[rt];
				trmn[rt<<1]+=lzy[rt];trmn[rt<<1|1]+=lzy[rt];
				lzy[rt<<1]+=lzy[rt];lzy[rt<<1|1]+=lzy[rt];
				lzy[rt]=0;
			}
		}
		void build(int rt,int l,int r,int aw){
			lzy[rt]=0;if(l==r){trmx[rt]=trmn[rt]=l*aw;return ;}int mid=l+r>>1;
			build(rt<<1,l,mid,aw);build(rt<<1|1,mid+1,r,aw);pushup(rt);
		}
		void modify(int rt,int l,int r,int al,int ar,int aw){
			if(l>r||l>ar||r<al||al>ar)return ;
			if(al<=l&&r<=ar){trmx[rt]+=aw;trmn[rt]+=aw;lzy[rt]+=aw;return ;}
			int mid=l+r>>1;pushdown(rt,l,r);
			if(al<=mid)modify(rt<<1,l,mid,al,ar,aw);
			if(ar>mid)modify(rt<<1|1,mid+1,r,al,ar,aw);
			pushup(rt);
		}
		int queryMax(int rt,int l,int r,int al,int ar){
			if(l>r||l>ar||r<al||al>ar)return -INF;int mid=l+r>>1,res=-INF;
			if(al<=l&&r<=ar)return trmx[rt];pushdown(rt,l,r);
			if(al<=mid)res=max(res,queryMax(rt<<1,l,mid,al,ar));
			if(ar>mid)res=max(res,queryMax(rt<<1|1,mid+1,r,al,ar));
			return res; 
		}
		int queryMin(int rt,int l,int r,int al,int ar){
			if(l>r||l>ar||r<al||al>ar)return INF;int mid=l+r>>1,res=INF;
			if(al<=l&&r<=ar)return trmn[rt];pushdown(rt,l,r);
			if(al<=mid)res=min(res,queryMin(rt<<1,l,mid,al,ar));
			if(ar>mid)res=min(res,queryMin(rt<<1|1,mid+1,r,al,ar));
			return res; 
		}
}T;
signed main(){
	read(n);
	for(int i=1;i<=n;i++)read(a[i]),vec[a[i]].push_back(i);
	T.build(1,0,n,-1);
	for(int i=n;i>0;i--){
		int siz=vec[i].size();
		for(int j=0;j<siz;j++)T.modify(1,0,n,vec[i][j],n,2);
		for(int j=0;j<siz;j++){
			int tmp=T.queryMax(1,0,n,vec[i][j],n)-T.queryMin(1,0,n,0,vec[i][j]-1);
			ans[vec[i][j]]=max(ans[vec[i][j]],tmp/2);
		}
	} 
	T.build(1,0,n,-1);
	for(int i=1;i<=n;i++){
		int siz=vec[i].size();
		for(int j=0;j<siz;j++)T.modify(1,0,n,vec[i][j],n,2);
		for(int j=0;j<siz;j++){
			int tmp=T.queryMax(1,0,n,vec[i][j],n)-T.queryMin(1,0,n,0,vec[i][j]-1)-1;
			ans[vec[i][j]]=max(ans[vec[i][j]],tmp/2);
		}
	} 
	for(int i=1;i<=n;i++)printf("%d ",ans[i]);puts("");
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值