P4755 Beautiful Pair(可持久化线段树+单调栈+启发式合并的思想)

小D有一个数列a。

当一个数对(i,j)满足a[i]*a[j]<=max[a[i],a[i+1],...a[j]]的时候

才被统计。

询问有多少对数对。

做法:

先单调栈处理出每个数作为最大值的区间,为了避免重复计算,这里对左边找第一个>=a[i]的数,对右边找第一个>a[i]的数,这样就可以去重。

然后对每个数,根据这个数的位置可以划分为左区间和右区间,对于这个东西可以每次枚举较小的那个区间,这样启发式的枚举,时间复杂度是O(nlognlogn)的

//对每个数处理出它作为最大值的左右区间
//然后枚举较短的那个区间计算较长的那个区间
//均摊时间复杂度O(nlogn)
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int M=maxn*40;
int n,a[maxn],L[maxn],R[maxn];
int T[maxn],c[M],lson[M],rson[M],tot;

int build (int l,int r) {
	int u=++tot;
	if (l==r) {
		c[u]=0;
		return u;
	}
	int mid=(l+r)>>1;
	lson[u]=build(l,mid);
	rson[u]=build(mid+1,r);
	c[u]=c[lson[u]]+c[rson[u]];
	return u;
} 
int up (int u,int l,int r,int p,int v) {
	int newRoot=++tot;
	c[newRoot]=c[u]+v;
	if (l==r) return newRoot;
	int mid=(l+r)>>1;
	lson[newRoot]=lson[u];
	rson[newRoot]=rson[u];
	if (p<=mid) lson[newRoot]=up(lson[u],l,mid,p,v);
	if (p>mid) rson[newRoot]=up(rson[u],mid+1,r,p,v);
	return newRoot; 
}
int query (int lr,int rr,int l,int r,int L,int R) {
	if (L>R) return 0;
	if (l>=L&&r<=R) return c[rr]-c[lr];
	int mid=(l+r)>>1;
	int ans=0;
	if (L<=mid) ans+=query(lson[lr],lson[rr],l,mid,L,R);
	if (R>mid) ans+=query(rson[lr],rson[rr],mid+1,r,L,R);
	return ans;
}
int t[maxn],mm;
int main () {
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d",a+i),t[i]=a[i],L[i]=1,R[i]=n;
	sort(t+1,t+n+1);
	mm=unique(t+1,t+n+1)-t-1;
	for (int i=1;i<=n;i++) a[i]=upper_bound(t+1,t+mm+1,a[i])-t-1;
	stack<int> st;
	for (int i=1;i<=n;i++) {
		while(st.size()) {
			if (a[st.top()]>=a[i]) {
				L[i]=st.top()+1;
				break;
			}
			st.pop();
		}
		st.push(i);
	}
	while (st.size()) st.pop();
	for (int i=n;i>=1;i--) {
		while (st.size()) {
			if (a[st.top()]>a[i]) {
				R[i]=st.top()-1;
				break;
			}
			st.pop();
		}
		st.push(i);
	}
	long long ans=0;
	T[0]=build(1,mm);
	//7 7 7 7 7
	for (int i=1;i<=n;i++) T[i]=up(T[i-1],1,mm,a[i],1);
	for (int i=1;i<=n;i++) {
		int x=i-L[i]+1;
		int y=R[i]-i+1;
		if (x<=y) {
			for (int j=L[i];j<=i;j++) {
				int re=(t[a[i]]/t[a[j]]);
				//printf("%d\n",re);
				int p=upper_bound(t+1,t+mm+1,re)-t-1;
				ans+=query(T[i-1],T[R[i]],1,mm,1,p);
			}
		}
		else {
			for (int j=i;j<=R[i];j++) {
				int re=(t[a[i]]/t[a[j]]);
				//printf("%d\n",re);
				int p=upper_bound(t+1,t+mm+1,re)-t-1;
				ans+=query(T[L[i]-1],T[i],1,mm,1,p);
			}
		}
	}
	printf("%lld\n",ans);
}
posted @ 2021-10-18 16:06  zlc0405  阅读(29)  评论(0编辑  收藏  举报