模拟赛时碰到了,感觉这个东西很优美啊,学习一下。
经典应用就是与区间最值算贡献相关的一类问题。
我们以 Special Segments
of Permutation 这道题为例讲解一下这种技巧。
首先,我们可以将所有区间分为以 \(p_1\)
为最大值的区间、以 \(p_2\)
为最大值的区间……我们可以在整个序列中划出极长的一段 \([l,r]\),这一段的最大值所处的位置为 \(mid\),则可以递归到 \([l,mid-1]\) 和 \([mid+1,r]\)
两端解决子问题。我们发现,笛卡尔树的结构完美符合递归的要求。我们可以考虑当前分治区间
\([l,r]\),区间最大值位置是 \(mid\),则只需在 \([l,mid]\) 和 \([mid,r]\) 各选取一个端点即可得到一个以
\(p_{mid}\)
为最大值的区间。这个过程有点像 cdq 分治的思想,总结一下:
找到区间最大值所在位置 \(mid\);
处理跨越 \(mid\)
的区间,此时处理的区间最大值一定是 \(p_{mid}\);
递归处理子区间 \([l,mid-1]\) 和
\([mid+1,r]\)。
考虑时间复杂度问题。由于区间不是对半均分,所以我们采用启发式的思想,保证统计答案时只枚举短区间,时间复杂度
\(O(n\log
n)\),否则时间复杂度可能退化至 \(O(n^2)\)。以本题为例,由于 \(p_i\)
各不相同,所以可以直接处理出每个值对应的位置,假设 \([l,mid]\) 这一段较短,那么在这个区间内枚举
\(i\),每次查询 \(p_{mid}-p_i\) 是否在区间 \([mid,r]\)
内即可。可以不显式建出笛卡尔树,保证递归结构正确即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| #include<cstdio> template<typename T> inline void read(T &x){ bool f=0;x=0;char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') f=1; ch=getchar(); } while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } if(f) x=~x+1; } template<typename T,typename...Args> void read(T &x,Args &...args){read(x);read(args...);} constexpr int N=2e5+10; int n,p[N],b[N],ans,P; struct node{ int pos,val; friend node operator+(const node &x,const node &y){ return x.val<y.val?y:x; } }tree[N<<2]; int query(int l,int r){ l+=P-1,r+=P+1; node res={}; while(l^1^r){ if(~l&1) res=res+tree[l^1]; if(r&1) res=res+tree[r^1]; l>>=1,r>>=1; } return res.pos; } void solve(int l,int r){ if(l>=r) return; int mid=query(l,r); solve(l,mid-1),solve(mid+1,r); if(mid-l<r-mid){ for(int i=l;i<=mid;i++){ int pos=b[p[mid]-p[i]]; ans+=(pos>=mid&&pos<=r); } } else{ for(int i=mid;i<=r;i++){ int pos=b[p[mid]-p[i]]; ans+=(pos>=l&&pos<=mid); } } } int main(){ read(n); P=1;while(P<=n+1) P<<=1; for(int i=1;i<=n;i++){ read(p[i]); b[p[i]]=i; tree[P+i]={i,p[i]}; } for(int i=P-1;i;i--) tree[i]=tree[i<<1]+tree[i<<1|1]; solve(1,n); printf("%d\n",ans); return 0; }
|
习题:
感谢 @_Kenma_ 提供题目!