题目链接:点击查看
题目大意:给出一个长度为 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;
}