P4755 Beautiful Pair (数据结构+分治)

Posted 昵称很长很长真是太好了

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4755 Beautiful Pair (数据结构+分治)相关的知识,希望对你有一定的参考价值。

题意:
小D有个数列a ,当一个数对 ( i , j ) ( i ≤ j ) (i,j)(i\\leq j) (i,j)(ij)满足 a i a_i ai a j ​ a_j​ aj 的积不大于 a i , a i + 1 ​ , . . . . . . . . . , a j ​ a_i, a_i+1​,.........,a_j​ ai,ai+1,.........,aj中的最大值时,小D认为这个数对是美丽的.请你求出美丽的数对的数量。
n ≤ 1 e 5 n \\leq1e5 n1e5
a i ≤ 1 e 9 a_i \\leq1e9 ai1e9
有人推荐笛卡尔树的练习题?但是怎么也想不到用笛卡尔树做…
跟在笛卡尔树上分治一样…只不过ST表换成了笛卡尔树
题解:
分治+主席树+ST表

每次找出区间 [ l , r ] [l,r] [l,r]之间最大值的下标 m i d mid mid(ST表查找即可)
然后枚举 l 到 m i d l到mid lmid的每个数,每次区间询问mid到r之间数小于等于 ⌊ a m i d a i ⌋ \\lfloor \\fraca_mida_i \\rfloor aiamid
但是我们会发现,极端情况下会被卡成 O ( n 2 l o g 2 n ) O(n^2log^2n) O(n2log2n)
那么我们借用启发式的思想,每次去枚举区间长度小的那一半,查询大的一半即可。
为什么这样做是正确的呢?
这其实就相当于枚举了每一个点,进行查询,并且不会出现重复的情况。
比如说当前查找的区间为 [ l , r ] [l,r] [l,r],查询出来的数对数量是跨过mid的数对数量,下一个查询的区间就是 [ l , m i d − 1 ] , [ m i d + 1 , r ] [l,mid-1],[mid+1,r] [l,mid1],[mid+1,r]这两个小区间了,因为这两个小区间根本不会存在跨过mid的数对,那个大区间也不存在不跨过的数对,所以不存在重复的情况。

代码:

#include<bits/stdc++.h>
#define int long long
#define endl '\\n'
using namespace std;

const int maxn=1e5+10;

int a[maxn],ans;
int sum[maxn*50];
int lc[maxn*50],rc[maxn*50];
int rt[maxn],cnt,n,p;

void update(int &node,int start,int ends,int lst,int pos)
    node=++cnt;
    lc[node]=lc[lst];
    rc[node]=rc[lst];
    sum[node]=sum[lst]+1;
    if(start==ends) return ;
    int mid=(start+ends)/2;
    if(pos<=mid) update(lc[node],start,mid,lc[lst],pos);
    else update(rc[node],mid+1,ends,rc[lst],pos);

int query(int start,int ends,int l,int r,int x,int y)
    if(l<=start&&ends<=r)
        return sum[y]-sum[x];
    
    int mid=(start+ends)>>1;
    int ans=0;
    if(l<=mid) ans+=query(start,mid,l,r,lc[x],lc[y]);
    if(mid<r) ans+=query(mid+1,ends,l,r,rc[x],rc[y]);
    return ans;


int stmax[maxn][21];
int mn[maxn];

int stcmpmax(int x,int y)
	return a[x]>a[y]?x:y;


void init()
    mn[0]=-1;
    for (int i=1;i<=n;i++)
        mn[i]=((i & (i-1))==0) ? mn[i-1]+1 : mn[i-1];
        stmax[i][0]=i;
    
    for (int j=1;j<=mn[n];j++)
        for (int i=1;i+(1<<j)-1<=n;i++)
        
            stmax[i][j]=stcmpmax(stmax[i][j-1],stmax[i+(1<<(j-1))][j-1]);
        

int rmq_max(int L,int R)

    int k=mn[R-L+1];
    return stcmpmax(stmax[L][k],stmax[R-(1<<k)+1][k]);



void divide(int l,int r)
    if(r<l) return ;
    if(l==r)
        if(a[l]==1) ans++;
        //cout<<"debug  "<<a[l]<<endl;
        return ;
    
    int pre=ans;
    int mid=rmq_max(l,r);
    //cout<<"debug  "<<mid<<endl;
    if(mid-l<r-mid)
        for(int i=l;i<=mid;i++)
            int nowx=a[mid]/a[i];
            ans+=query(1,p,1,nowx,rt[mid-1],rt[r]);
        
    
    else
        for(int i=mid;i<=r;i++)
            int nowx=a[mid]/a[i];
            ans+=query(1,p,1,nowx,rt[l-1],rt[mid]);
        
    
    //cout<<"debug  "<<l<<" "<<r<<" "<<mid<<" "<<ans-pre<<endl;
    divide(l,mid-1);
    divide(mid+1,r);


signed main()
    ios::sync_with_stdio(false);
    cin.tie(0);
    //freopen("out.txt","w",stdout);
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
        p=max(p,a[i]);
    
    init();
    for(int i=1;i<=n;i++)
        update(rt[i],1,p,rt[i-1],a[i]);
    

    divide(1,n);
    cout<<ans<<endl;


以上是关于P4755 Beautiful Pair (数据结构+分治)的主要内容,如果未能解决你的问题,请参考以下文章

P4755 Beautiful Pair

P4755 Beautiful Pair 笛卡尔树+主席树

luoguP4755 Beautiful Pair

luoguP4755 Beautiful Pair 笛卡尔树+线段树合并+启发式合并

luoguP4755 Beautiful Pair 笛卡尔树+线段树合并+启发式合并

Codeforces 55D - Beautiful numbers 数位dp