#线段树#洛谷 12401 [COI 2025] 玻利维亚 / Bolivija

题目传送门


分析

对于对称的一对高度 \(x,y(x<y)\)\(x\leq A<B\leq y\) 的二元组都不可以选。

不妨将 \((A,B)\) 二元组的个数转化为 \((A+1,B)\) 二元组的个数,那么 \(x+1\leq A+1\leq B\leq y\)

相当于 \([x+1,y]\) 的点不可以选,二元组个数为 \([1,a[\frac{n+1}{2}]]\) 中极长可选连续段的长度 \(\sum \binom{len+1}{2}\)

那么就将 \([x+1,y]\) 的每个位置加 \(1\),只有为 \(0\) 的时候才能统计极长连续段,问题是这样 pushup 的时候会非常困难。

不妨改为只有为最小值的时候才能统计极长连续段,这时只需判断全局最小值是否为 \(0\) 就能判断答案的合法性,

线段树里需要维护左右端点开始的极长连续段长度以及点覆盖次数的最小值,顺便统计答案,需要对左右区间最小值大小关系分类讨论。


代码

#include <iostream>
#include <algorithm>
using namespace std;
const int N=700011; long long w[N<<2];
int mn[N<<2],lazy[N<<2],wl[N<<2],wr[N<<2],n,Q,a[N];
long long Cn2(int x){return x*(x+1ll)>>1;}
void ptag(int k,int z){mn[k]+=z,lazy[k]+=z;}
void pup(int k,int l,int mid,int r){
	if (mn[k<<1]<mn[k<<1|1])
		w[k]=w[k<<1],wl[k]=wl[k<<1],wr[k]=0,mn[k]=mn[k<<1];
	else if (mn[k<<1]>mn[k<<1|1])
	    w[k]=w[k<<1|1],wl[k]=0,wr[k]=wr[k<<1|1],mn[k]=mn[k<<1|1];
	else{
		mn[k]=mn[k<<1],wl[k]=wl[k<<1],wr[k]=wr[k<<1|1];
		if (wl[k<<1]==mid-l+1) wl[k]+=wl[k<<1|1];
		if (wr[k<<1|1]==r-mid) wr[k]+=wr[k<<1];
		w[k]=w[k<<1]+w[k<<1|1]-Cn2(wr[k<<1])-Cn2(wl[k<<1|1])+Cn2(wr[k<<1]+wl[k<<1|1]);
	}
}
void build(int k,int l,int r){
	if (l==r){
		mn[k]=0,w[k]=wl[k]=wr[k]=1;
		return;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	pup(k,l,mid,r);
}
void update(int k,int l,int r,int x,int y,int z){
	if (l==x&&r==y){
		ptag(k,z);
		return;
	}
	if (lazy[k]){
		ptag(k<<1,lazy[k]);
		ptag(k<<1|1,lazy[k]);
		lazy[k]=0;
	}
	int mid=(l+r)>>1;
	if (y<=mid) update(k<<1,l,mid,x,y,z);
	else if (x>mid) update(k<<1|1,mid+1,r,x,y,z);
	else{
		update(k<<1,l,mid,x,mid,z);
		update(k<<1|1,mid+1,r,mid+1,y,z);
	}
	pup(k,l,mid,r);
}
void upd(int x,int y,int z){
	if (x==y) return;
	if (x>y) swap(x,y);
	update(1,1,a[(n+1)>>1],x+1,y,z);
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>Q;
	for (int i=1;i<=n;++i) cin>>a[i];
	build(1,1,a[(n+1)>>1]);
	for (int i=1;i<=n/2;++i) upd(a[i],a[n-i+1],1);
	if (mn[1]>0) cout<<0<<'\n';
	    else cout<<w[1]<<'\n';
	for (int i=1;i<=Q;++i){
		int x,z; cin>>x>>z;
		upd(a[x],a[n-x+1],-1),a[x]=z;
		upd(a[x],a[n-x+1],1);
		if (mn[1]>0) cout<<0<<'\n';
		    else cout<<w[1]<<'\n';
	}
	return 0;
}
posted @ 2025-12-02 20:46  lemondinosaur  阅读(3)  评论(0)    收藏  举报