CF1556E-Equilibrium【棧,樹狀陣列】
阿新 • • 發佈:2021-08-30
正題
題目連線: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; }