【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;
}