洛谷 - P4755 Beautiful Pair(笛卡尔树+主席树)

题目链接:点击查看

题目大意:给出一个长度为 n 的数列 a,现在一个数对 ( i , j ) 如果满足 a[ i ] * a[ j ] <=max( a[ i ] ~ a[ j ] ),则称其为美丽的,求出美丽对的数量

题目分析:直接统计是比较困难的,但正难则反,我们可以枚举每个数作为最大值时,在其管辖的区间内进行统计,对于某个最大值 i 来说,肯定存在着 l[ i ] 和 r[ i ] ,使得 l[ i ] - 1 是 i 左侧首个大于 a[ i ] 的位置,r[ i ] + 1 是 i 右侧首个大于 a[ i ] 的位置,这样在区间 [ l[ i ] , r[ i ] ] 中,跨过位置 i 时的最大值一定是 a[ i ] ,于是区间就被分成了两段:[ l[ i ] , i ] , [ i , r[ i ] ],因为乘积运算满足交换律,所以遍历长度较小的一段,在另一段中查找符合条件的元素的个数即可,假设当前枚举的数字为 a[ j ] ,最大值为 a[ i ] ,所以需要在区间内查找 [ 1 , a[ i ] / a[ j ] ] 的元素个数,这显然是线段树的功能,不过如果放在区间上询问的话,就需要可持久化一下来使用,也就是主席树了,对于上述找 l[ i ] 和 r[ i ] 的过程,可以用笛卡尔树来完成,然后 dfs 一遍统计答案就好了

代码:
 

#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
using namespace std;
 
typedef long long LL;
 
typedef unsigned long long ull;
 
const int inf=0x3f3f3f3f;
 
const int N=1e5+100;

int a[N];

LL ans;
/*主席树*/ 
struct Node1
{
	int l,r;
	int sum;
}tree[N*35];
 
int cnt,root[N];
 
void update(int num,int &k,int l,int r)
{
	tree[cnt++]=tree[k];
	k=cnt-1;
	tree[k].sum++;
	if(l==r)
		return;
	int mid=l+r>>1;
	if(num<=mid)
		update(num,tree[k].l,l,mid);
	else
		update(num,tree[k].r,mid+1,r);
}
 
int query(int i,int j,int L,int R,int l,int r)//i:左端点根节点编号,j:右端点根节点编号,l,r:目标区间,L,R:当前区间 
{
	if(l>r)
		return 0;
	if(R<l||L>r)
		return 0;
	if(L>=l&&R<=r)
		return tree[j].sum-tree[i].sum;
	int mid=L+R>>1;
	return query(tree[i].l,tree[j].l,L,mid,l,r)+query(tree[i].r,tree[j].r,mid+1,R,l,r);
}
/*主席树*/
/*笛卡尔树*/
struct Node2
{
	int l,r,val;
}t[N];

stack<int>s;
 
void insert(int x)
{
	while(s.size()&&t[s.top()].val<t[x].val)
		s.pop();
	t[x].l=t[s.top()].r;//x->lson
	t[s.top()].r=x;//fa->x(rson)
	s.push(x);
}
/*笛卡尔树*/
void dfs(int k,int l,int r)
{
	if(k==0)//特判一下根 
	{
		if(t[k].l)
			dfs(t[k].l,l,r);
		if(t[k].r)
			dfs(t[k].r,l,r);
		return;
	}
	if(k-l<=r-k)//遍历左区间 
		for(int i=l;i<=k;i++)
			ans+=query(root[k-1],root[r],1,inf,1,t[k].val/t[i].val);
	else//遍历右区间 
		for(int i=k;i<=r;i++)
			ans+=query(root[l-1],root[k],1,inf,1,t[k].val/t[i].val);
	if(t[k].l)
		dfs(t[k].l,l,k-1);
	if(t[k].r)
		dfs(t[k].r,k+1,r);
}

void init()
{
	t[0].val=inf;
	t[0].l=t[0].r=0;
	s.push(0);
	root[0]=0;
	tree[0].l=tree[0].r=tree[0].sum=0;
	cnt=1;
}

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	init();
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&t[i].val);
		insert(i);
		root[i]=root[i-1];
		update(t[i].val,root[i],1,inf);
	}
	dfs(0,1,n);
	printf("%lld\n",ans);



















   return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/108195291