欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

洛谷4755 Beautiful Pair 分治 主席树 离散化

程序员文章站 2022-05-08 17:43:11
...

题目链接

题意:
给你一个长度为nn的序列,问存在多少对(i,j)(i,j),满足aiaj<=max{ai,ai+1...aj}a_i*a_j<=max\{a_i,a_{i+1}...a_{j}\}。也就是有多少个位置对满足序列上的值的乘积小于等于区间最大值。n<=1e5n<=1e5

题解:
这个题想了半天,感觉可能作为切入点的还是这个最大值。我们的一个想法是找到当前的区间最大值,然后分治。当前层统计跨过最大值的答案,不跨过的递归到两个子区间。这样对于随机数据是对的,因为最大值期望的出现位置比较接近中间。但是精心构造的话,递归层数会变成O(n)O(n)量级,可能每一层处理一遍都要O(n)O(n)或者O(nlogn)O(nlogn)的话,复杂度就不对了。

那么我们应该怎么办呢?这里就是这个题的关键所在了。对于一般的题目,对于最值分治一般是复杂度不对的,应该是只有在数据随机的情况下复杂度是对的。但是这个题不保证数据随机,就要想别的办法来保证复杂度了。我们考虑到,对于当前递归的区间,我们既可以枚举最大值前面的每个数,统计后面的一半有多少个数可以与前面形成合法的数对,也可以枚举后半部分的每一个数来考虑前面的数对后面的数的贡献。那么我们按照这样一个策略来枚举:我们看最值两侧哪一侧的数少,就枚举哪一侧,这样就能保证复杂度。原因是,每个数每被枚举一次,那么这个区间的大小就会至少增大到原来的两倍,所以每个数最多被枚举loglog次,总枚举量是nlognnlogn级别的。另外我们还要维护一个区间内在某个权值范围内的数的个数,这个东西可以用一个主席树来维护。

其他的都是一些常规操作了,不懂可以看看代码,我不想赘述了。

代码:

#include <bits/stdc++.h>
using namespace std;

int n,a[100010],b[100010],cnt,root[100010],shu;
long long ans;
struct node
{
	int l,r,mx,id;
}tr1[400010];
struct tree
{
	int l,r,sz;
}tr2[2000010];
inline int read()
{
	int x=0;
	char s=getchar();
	while(s>'9'||s<'0')
	s=getchar();
	while(s>='0'&&s<='9')
	{
		x=x*10+s-'0';
		s=getchar();
	}
	return x;
}
inline void build1(int rt,int l,int r)
{
	tr1[rt].l=l;
	tr1[rt].r=r;
	tr1[rt].mx=0;
	tr1[rt].id=0;
	if(l==r)
	{
		tr1[rt].mx=a[l];
		tr1[rt].id=l;
		return;
	}
	int mid=(l+r)>>1;
	build1(rt<<1,l,mid);
	build1(rt<<1|1,mid+1,r);
	tr1[rt].mx=max(tr1[rt<<1].mx,tr1[rt<<1|1].mx);
	if(tr1[rt<<1].mx>=tr1[rt<<1|1].mx)
	tr1[rt].id=tr1[rt<<1].id;
	else
	tr1[rt].id=tr1[rt<<1|1].id;
}
inline void build2(int &rt,int l,int r)
{
	if(!rt)
	rt=++shu;
	tr2[rt].sz=0;
	if(l==r)
	return;
	int mid=(l+r)>>1;
	build2(tr2[rt].l,l,mid);
	build2(tr2[rt].r,mid+1,r);
}
inline void update(int &rt,int rt1,int l,int r,int x)
{
	rt=++shu;
	tr2[rt]=tr2[rt1];
	tr2[rt].sz++;
	if(l==r)
	return;
	int mid=(l+r)>>1;
	if(x<=mid)
	update(tr2[rt].l,tr2[rt1].l,l,mid,x);
	else
	update(tr2[rt].r,tr2[rt1].r,mid+1,r,x);
}
inline node query1(int rt,int le,int ri)
{
	int l=tr1[rt].l,r=tr1[rt].r;
	if(le<=l&&r<=ri)
	return tr1[rt];
	int mid=(l+r)>>1;
	node res,ji1,ji2;
	ji1.mx=0;
	ji1.id=0;
	ji1.l=0;
	ji1.r=0;
	ji2.mx=0;
	ji2.id=0;
	ji2.l=0;
	ji2.r=0;
	res.mx=0;
	res.id=0;
	res.l=0;
	res.r=0;
	if(le<=mid)
	ji1=query1(rt<<1,le,ri);
	if(mid+1<=ri)
	ji2=query1(rt<<1|1,le,ri);
	if(ji1.mx>=ji2.mx)
	res=ji1;
	else
	res=ji2;
	return res;
}
inline int query(int rt1,int rt2,int l,int r,int x)
{
	if(r<=x)
	return tr2[rt1].sz-tr2[rt2].sz;
	int mid=(l+r)>>1,res=0;
	res+=query(tr2[rt1].l,tr2[rt2].l,l,mid,x);
	if(mid+1<=x)
	res+=query(tr2[rt1].r,tr2[rt2].r,mid+1,r,x);
	return res;
}
inline void solve(int l,int r)
{
	if(l>r)
	return;
	if(l==r)
	{
		if(b[a[l]]==1)
		++ans;
		return;
	}
	node ji=query1(1,l,r);
	int mid=ji.id,mx=b[ji.mx];
	if(r-mid>mid-l+1)//枚举前面 
	{
		for(int i=l;i<=mid;++i)
		{
			int x=upper_bound(b+1,b+cnt+1,mx/b[a[i]])-b-1;
			if(x)
			ans+=query(root[r],root[mid-1],1,cnt,x);
		}
	}
	else//枚举后面 
	{
		for(int i=mid;i<=r;++i)
		{
			int x=upper_bound(b+1,b+cnt+1,mx/b[a[i]])-b-1;
			if(x)
			ans+=query(root[mid],root[l-1],1,cnt,x);
		}
	}
	solve(l,mid-1);
	solve(mid+1,r);
} 
int main()
{
	n=read();
	for(int i=1;i<=n;++i)
	{
		a[i]=read();
		b[i]=a[i]; 
	} 
	sort(b+1,b+n+1);
	cnt=unique(b+1,b+n+1)-b-1;
	for(int i=1;i<=n;++i)
	a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
	build1(1,1,n);
	build2(root[0],1,cnt);		
	for(int i=1;i<=n;++i)
	update(root[i],root[i-1],1,cnt,a[i]);
	solve(1,n);
	printf("%lld\n",ans);
	return 0;
}