CF1556E-Equilibrium【栈,树状数组】
正题
题目连接:https://codeforces.com/contest/1556/problem/E
题目大意
两个长度为\(n\)的序列\(a,b\),\(q\)次询问一个区间\([l,r]\)。
在这个区间中你每次可以选择一个长度为偶数的下标递增的序列,让奇数位置的\(a\)加一,偶数位置的\(b\)加\(1\)。
求最少操作次数使得每个\(a_i=b_i\)。
\(n,q\leq 10^5\)
解题思路
视为一个减一个加的话,令\(x_i=b_i-a_i\)这样就变成了每个需要加/减的次数。
可以视为每个减后面需要跟一个加,加前面需要跟一个减,而加后面可以免费跟一个减。
把需要减的看成\((\),需要加的看成\()\)的话就能看出来无论什么区间的情况下每个\((\)都是和同一个\()\)匹配或者无法匹配。
离线询问,开两个栈分别存\((\)和\()\),然后一个树状数组用来记录每个位置需要的左端点位置上限,另一个记录每个左端点对应的权值即可。
时间复杂度\(O(n\log n+q\log n)\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#define ll long long
#define lowbit(x) (x&-x)
using namespace std;
const ll N=1e5+10;
struct node{
ll l,r,id;
}q[N];
ll n,m,a[N],c[N],d[N],t[N],s[N],ans[N];
stack<int> S,T;
void Change(ll x,ll val){
x=n-x+1;
while(x<=n){
t[x]+=val;
x+=lowbit(x);
}
return;
}
ll Ask(ll x){
ll ans=0;x=n-x+1;
while(x){
ans+=t[x];
x-=lowbit(x);
}
return ans;
}
void Dhange(ll x,ll val){
x=n-x+1;
while(x<=n){
s[x]=min(s[x],val);
x+=lowbit(x);
}
return;
}
ll Bsk(ll x){
ll ans=n+1;x=n-x+1;
while(x){
ans=min(ans,s[x]);
x-=lowbit(x);
}
return ans;
}
bool cmp(node x,node y)
{return x.r<y.r;}
signed main()
{
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(ll i=1,x;i<=n;i++)
scanf("%lld",&x),a[i]=x-a[i];
for(ll i=1;i<=m;i++)
scanf("%lld%lld",&q[i].l,&q[i].r),q[i].id=i;
sort(q+1,q+1+m,cmp);
memset(s,0x3f,sizeof(s));
c[0]=1e18;S.push(0);
for(ll i=1,z=1;i<=n;i++){
if(a[i]>0){
ll x=a[i];Change(i,x);
while(!T.empty()&&d[T.top()]<x)
x-=d[T.top()],Change(T.top(),-d[T.top()]),T.pop();
if(!T.empty())d[T.top()]-=x,Change(T.top(),-x);
S.push(i);c[i]=a[i];
}
if(a[i]<0){
ll x=-a[i];
while(c[S.top()]<x)
x-=c[S.top()],S.pop();
c[S.top()]-=x;Dhange(i,S.top());
if(c[S.top()]==0)S.pop();
T.push(i);d[i]=-a[i];
}
while(z<=m&&q[z].r==i){
if(q[z].l<=S.top()||q[z].l>Bsk(q[z].l))ans[q[z].id]=-1;
else ans[q[z].id]=Ask(q[z].l);
z++;
}
if(z>m)break;
}
for(ll i=1;i<=m;i++)
printf("%lld\n",ans[i]);
return 0;
}