题解[LuoguP4755 Beautiful Pair]

题目

Luogu

Sol

提供一个可能不那么正常的想法。

tags:笛卡尔树+分治

先根据原序列建一棵笛卡尔树出来,以位置为第一关键值(满足二叉查找树性质),权值为第二关键字(满足大根堆性质)。

对于一个区间\([l,r]\):

我们先找到区间中的最大值,设它的位置为\(pos\)

我们现在只考虑\([pos+1,r]\)\([l,pos-1]\)的贡献,也就是跨过了最大值的贡献。

我们把\([l,pos-1]\)从大到小排序,\([pos+1,r]\)从小到大排序。

由于左区间递减,所以\(\frac{a_{l_1}}{a_{pos}}\)递增,满足单调性。

可以用两个指针\(l_1,l_2\)分别在最大值左边和右边的区间移动,每次把右边区间小于等于\(\frac{a_{l_1}}{a_{pos}}\)的数计入答案。

(这里带了一点\(CDQ\)的思想)

记得最后要还原区间,不然往下递归时会出错。

那最大值\(pos\)的贡献怎么算?

很简单,统计整个区间一的个数就可以了。

Code

还挺短的

#include<bits/stdc++.h>
#define N (100010)
#define ll long long
using namespace std;
struct xbk{int id;ll v;}a[N];
int n,rt,ls[N],rs[N];
ll ans,sum[N];
vector<int>v;
inline ll read(){
	ll w=0;
	char ch=getchar();
	while(ch>'9'||ch<'0') ch=getchar();
	while(ch>='0'&&ch<='9'){
		w=(w<<3)+(w<<1)+(ch^48);
		ch=getchar();
	}
	return w;	
}
inline bool cmp1(xbk a,xbk b){return a.id<b.id;}
inline bool cmp2(xbk a,xbk b){return a.v<b.v;}
inline bool cmp3(xbk a,xbk b){return a.v>b.v;}
inline void build(){
	for(int i=1;i<=n;i++){
		int j=0;
		while(v.size()&&a[v.back()].v<a[i].v) j=v.back(),v.pop_back();
		if(!v.size()) rt=i;
		else rs[v.back()]=i;
		ls[i]=j;
		v.push_back(i);
	}
	return;
}
inline void binary(int st,int l,int r){
	int ll1=st-1,rr1=st+1,flag=0;
	ans+=sum[r]-sum[l-1];
	if(ll1>=l&&r>=rr1){
		flag=1;
		sort(a+l,a+ll1+1,cmp3),sort(a+rr1,a+r+1,cmp2);
		for(int l1=l,l2=rr1;l1<=ll1;l1++){
			ll val=a[st].v/a[l1].v;
			while(a[l2].v<=val&&l2<=r) l2++;
			ans+=l2-rr1;
		}
	}
	if(flag) sort(a+l,a+r+1,cmp1);
	if(ls[st]) binary(ls[st],l,st-1);
	if(rs[st]) binary(rs[st],st+1,r);
	return;
}
int main(){
	n=read();
	for(int i=1;i<=n;i++){
		a[i].v=read(),a[i].id=i;
	    sum[i]=sum[i-1]+(a[i].v==1);
	}
	build();
	binary(rt,1,n);
	printf("%lld\n",ans);
	return 0;
}

完结撒花❀

posted @ 2021-05-27 14:54  xxbbkk  阅读(73)  评论(0编辑  收藏  举报