1. 程式人生 > 其它 >CF1556E-Equilibrium【棧,樹狀陣列】

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