数据结构 笛卡尔树+树状数组 CF1117G题解

题意:区间建笛卡尔树,求每个节点的siz之和。

首先看到笛卡尔树,就应该想到,因为这是一个排列,可以找到通过左边和右边第一个比自己大的元素来“建立”笛卡尔树。

设 \(l(u)\) 为下标是 \(u\) 的元素左边第一个比自身大的元素,\(r(u)\) 同理。

答案就是

\[\sum_{i=L}^R \min(r(i)-1,R)-\max(l(i)+1,L)+1 \]

将两部分拆开计算,本质是一样的。下面以右端为例

对于一个 \(\sum\),答案由两部分组成:

  1. \(r(i)\)
  2. \(R\)

我们只需要对其分别求和就好了

我们从 \(n\) 扫到 \(1\),对于一个 \(r(i)\) 会在扫到 \(r(i)\) 时变成 \(R\),且只会变一次。

用两颗树状数组和即可。复杂度 \(O(n\log n)\)

code:

#include<cstdio>
#include<vector>
const int M=1e6+6;
int n,m,a[M],l[M],r[M],L[M],R[M],num[M];long long BIT[M],ans[M];
std::vector<int>qL[M],qR[M],idL[M],idR[M];
int top,stk[M];
inline void Add1(int x,int val){
	for(;x<=n;x+=1<<__builtin_ctz(x))BIT[x]+=val;
}
inline void Add2(int x,int val){
	for(;x<=n;x+=1<<__builtin_ctz(x))num[x]+=val;
}
inline long long Query1(int x){
	long long ans=0;
	for(;x>=1;x-=1<<__builtin_ctz(x))ans+=BIT[x];
	return ans;
}
inline int Query2(int x){
	int ans=0;
	for(;x>=1;x-=1<<__builtin_ctz(x))ans+=num[x];
	return ans;
}
signed main(){
	register int i,x;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;++i)scanf("%d",a+i);
	for(i=1;i<=n;++i){
		while(top&&a[i]>=a[stk[top]])--top;
		idL[(L[i]=top?stk[top]:0)+1].push_back(i);stk[++top]=i;
	}
	top=0;
	for(i=n;i>=1;--i){
		while(top&&a[i]>=a[stk[top]])--top;
		idR[(R[i]=top?stk[top]:n+1)-1].push_back(i);stk[++top]=i;
	}
	for(i=1;i<=m;++i)scanf("%d",l+i),qL[l[i]].push_back(i);
	for(i=1;i<=m;++i)scanf("%d",r+i),qR[r[i]].push_back(i);
	for(i=1;i<=n;++i)Add1(i,R[i]-1);
	for(i=n;i>=1;--i){
		for(int&id:idR[i])Add1(id,-R[id]+1),Add2(id,1);
		for(int&id:qR[i]){
			ans[id]+=(Query1(r[id])-Query1(l[id]-1))+1ll*i*(Query2(r[id])-Query2(l[id]-1));
		}
	}
	for(i=1;i<=n;++i)BIT[i]=num[i]=0;
	for(i=1;i<=n;++i)Add1(i,L[i]+1);
	for(i=1;i<=n;++i){
		for(int&id:idL[i])Add1(id,-L[id]-1),Add2(id,1);
		for(int&id:qL[i]){
			ans[id]-=(Query1(r[id])-Query1(l[id]-1))+1ll*i*(Query2(r[id])-Query2(l[id]-1));
		}
	}
	for(i=1;i<=m;++i)printf("%lld ",ans[i]+r[i]-l[i]+1);
}
上一篇:IOS OC项目的单例模式


下一篇:OC 基础 UITableView