【CF1556E】Equilibrium

题目

题目链接:https://codeforces.com/contest/1556/problem/E
给定两个长度为 \(n\) 的序列 \(a,b\)。一次操作可以选择一个严格递增序列 \(q_1,q_2,\cdots,q_k\)(要求 \(k\) 为偶数),让 \(a_{q_1},a_{q_3},\cdots,a_{q_{k-1}}\) 全部加 \(1\)\(a_{q_2},a_{q_4},\cdots,a_{q_{k}}\) 全部减 \(1\)
\(Q\) 次询问,每次询问给定 \(l,r\),求区间 \([l,r]\) 这一段区间至少需要多少次操作才能把 \(a_{l}\sim a_r\) 变为 \(b_l\sim b_r\)
\(n,Q\leq 10^5\)

思路

首先令 \(a_i= b_i-a_i\)。不难发现,对于正数,我们只可能让他减;负数只可能加;零肯定不会选。这个画一下很容易证明。
那么把正数 \(v\) 看作连续 \(v\)(,负数 \(v\) 看作连续 \(-v\)),那么对于一次询问 \(l,r\),它是可能的当且仅当 \(l\sim r\) 这一段是一个合法的括号序列,并且答案就是最大的括号嵌套数。
考虑类似区间最大子段和一般的线段树做法,维护一棵线段树,对于线段树上每一个区间维护 \(ans,cnt1,cnt2\),分别表示这个区间内最大括号嵌套层数;剩余没有匹配的左 / 右括号数量。
区间合并的时候就判断一下左区间的 ( 数量和右区间的 ) 数量的大小。
时间复杂度 \(O(Q\log n)\)

代码

#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;

const int N=100010;
int Q,n,a[N],b[N];

struct node
{
	ll ans,cnt1,cnt2;
};

node merge(node x,node y)
{
	node z;
	if (x.cnt1>y.cnt2)
	{
		z.ans=max(x.ans,y.ans+x.cnt1-y.cnt2);
		z.cnt1=x.cnt1+y.cnt1-y.cnt2;
		z.cnt2=x.cnt2;
	}
	else
	{
		z.ans=max(y.ans,x.ans+y.cnt2-x.cnt1);
		z.cnt1=y.cnt1;
		z.cnt2=x.cnt2+y.cnt2-x.cnt1;
	}
	return z;
}

struct SegTree
{
	node f[N*4];
	
	void update(int x,int l,int r,int k,ll v)
	{
		if (l==r)
		{
			f[x]=(node){abs(v),max(0LL,v),max(0LL,-v)};
			return;
		}
		int mid=(l+r)>>1;
		if (k<=mid) update(x*2,l,mid,k,v);
			else update(x*2+1,mid+1,r,k,v);
		f[x]=merge(f[x*2],f[x*2+1]);
	}
	
	node query(int x,int l,int r,int ql,int qr)
	{
		if (ql==l && qr==r) return f[x];
		int mid=(l+r)>>1;
		if (qr<=mid) return query(x*2,l,mid,ql,qr);
		if (ql>mid) return query(x*2+1,mid+1,r,ql,qr);
		return merge(query(x*2,l,mid,ql,mid),query(x*2+1,mid+1,r,mid+1,qr));
	}
}seg;

int main()
{
	scanf("%d%d",&n,&Q);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int i=1;i<=n;i++) scanf("%d",&b[i]);
	for (int i=1;i<=n;i++) seg.update(1,1,n,i,b[i]-a[i]);
	while (Q--)
	{
		int l,r;
		scanf("%d%d",&l,&r);
		node res=seg.query(1,1,n,l,r);
		if (res.cnt1 || res.cnt2) cout<<"-1\n";
			else cout<<res.ans<<"\n";
	}
	return 0;
}
posted @ 2021-08-30 15:15  stoorz  阅读(49)  评论(0编辑  收藏  举报