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

题目链接

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

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

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

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

代码:

#include 
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;
}

你可能感兴趣的:(数据结构,主席树,离散化,分治)