• 【题解】P4755 Beautiful Pair(启发式合并的思路+分治=启发式分治)


    【题解】P4755 Beautiful Pair

    upd: 之前一个first second烦了,现在AC了

    由于之前是直接抄std写的,所以没有什么心得体会,今天自己写写发现

    不知道为啥(90)

    我直接把之前写的总结kuai过来

    而选取其他位置(比如序列的最大值)由不能保证复杂度。但是如果每层分治的复杂度只与较小的一侧的大小有关,那么这个复杂度就等同于启发式合并的复杂度。

    一句话证明启发式合并的复杂度:一次合并至少有一个集合倍增了。

    Luogu4755 Beautiful Pair((max)分治)

    (max)分治意思就是分治的时候不好取中间数作为分治中心,要选择别的特殊数进行分治,此时可以利用启发式合并的思想做到外层一个(log)

    给一个序列 ({a_i }),求有多少对 (1 ≤ i ≤ j ≤ n) 满足 (a_i a_ j ≤ max _{k=i}^j a_ k)

    考虑这样的做法,如何分治求这种数对,考虑这样的做法:

    分治处理区间:长度为(n,[l,r])并且跨过([l,r])某个最大值(有多个最大值的时候随意指定一个)的所有答案区间个数:

    首先随便一个数据结构维护出一段区间的最大值的位置(多个的时候随便指定一个),就把这个([l,r])分成了两个区间递归下去即可。 现在复杂度的关键在于处理当前层的复杂度了,受到启发式合并的启发(大雾),我们考虑大段维护,小的朴素,考虑这样的做法:

    首先对于短的那一边区间的长度必定(le n/2),现在先枚举这半边区间的(a_u),设当前层的全局最大值为(k),那么我们现在要查询另一边区间中,小于等于(lfloor dfrac {k}{a_u} floor)(a_i)个数,这个东西直接值域主席树维护一波。主席树是一个log的。这样统计当前层的答案的复杂度是(f(n)=O(dfrac n 2 log L))。由于每层的复杂度和当前层短的那一边有关,利用启发式合并的复杂度证明方法很容易发现这样的复杂度是(O(n log^2n))的,只要倒着看我们这个分治的递归就行了。

    //@winlere
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #define mid ((l+r)>>1)
    #define lef l,mid,seg[pos].ls
    #define rgt mid+1,r,seg[pos].rs
    #define pp(x) seg[x].val=seg[seg[x].ls].val+seg[seg[x].rs].val
    using namespace std;  typedef long long ll;   typedef const int& cint;
    inline int qr(){
          register int ret=0,f=0;
          register char c=getchar();
          while(c<48||c>57)f|=c==45,c=getchar();
          while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
          return f?-ret:ret;
    }
    const int maxn=1e5+5;
    pair<int,int> st[maxn][19];
    int lg[maxn],n,data[maxn],sav[maxn],len;   ll ans;
    inline pair<int,int> que(int l,int r){return max(st[l][lg[r-l]],st[r-(1<<lg[r-l])+1][lg[r-l]]);}
    struct Chairseg{
          struct E{
    	    int ls,rs,val;
    	    E(){ls=rs=val=0;} E(cint a,cint b,cint c){ls=a; rs=b; val=c;}
          }seg[maxn*60];
          int rt[maxn],cnt,n;
          void build(int k,int val,int l,int r,int pos,int last){
    	    if(l==r){seg[pos].val=seg[last].val+val;return;}
    	    seg[pos]=seg[last];
    	    if(k<=mid) seg[pos].ls=++cnt,build(k,val,lef,seg[last].ls);
    	    if(k> mid) seg[pos].rs=++cnt,build(k,val,rgt,seg[last].rs);
    	    pp(pos);
          }
          int que(int L,int R,int l,int r,int pos){
    	    if(L>r||R<l||r<0) return 0;
    	    if(L<=l&&r<=R) return seg[pos].val;
    	    return que(L,R,lef)+que(L,R,rgt);
          }
          ll que(int L,int R,int l,int r){return que(l,r,1,n,rt[R])-que(l,r,1,n,rt[L-1]);}
          inline void init(const int&n){this->n=n;}
    }T;
    inline int divd(const int&x){return upper_bound(sav+1,sav+len+1,x)-sav-1;}
    void divd(int l,int r){
          if(l>=r) return ans+=ll(l==r)*(data[l]==1),void();
          const pair<int,int>&now=que(l,r);
          if(now.second<mid)
    	    for(int t=l;t<=now.second;++t)
    		  ans=ans+T.que(now.second,r,1,divd(now.first/data[t]));
          else
    	    for(int t=now.second;t<=r;++t)
    		  ans=ans+T.que(l,now.second,1,divd(now.first/data[t]));
          divd(l,now.second-1); divd(now.second+1,r);
    }
    int main(){
          n=qr();
          for(int t=1;t<=n;++t) lg[t]=lg[t-1]+(1<<lg[t-1]<<1==t);
          for(int t=1;t<=n;++t) st[t][0]=make_pair(data[t]=qr(),t),sav[t]=data[t];
          for(int t=1;t<=lg[n];++t)
    	    for(int i=1;i<=n;++i)
    		  st[i][t]=max(st[i][t-1],st[min(i+(1<<t>>1),n)][t-1]);
          sav[n+1]=1e9+1;
          sort(sav+1,sav+n+2);
          len=unique(sav+1,sav+n+2)-sav-1;
          T.init(len);
          for(int t=1;t<=n;++t) T.build(divd(data[t]),1,1,T.n,T.rt[t]=++T.cnt,T.rt[t-1]);
          divd(1,n);
          printf("%lld
    ",ans);
          return 0;
    }
    
    
  • 相关阅读:
    Delphi的对话框窗体
    Delphi中窗体的事件
    TForm类有关属性简介
    Delphi的工具栏
    一个简单的MDI示范程序(Delphi)
    Delphi的组件选项卡(Component Palette)
    最简单的多重窗体的应用(Delphi)
    发现一个SVG做的地图网站:ChinaQuest
    计算最近点和最近线段
    寻找MapBar的地图切割方法
  • 原文地址:https://www.cnblogs.com/winlere/p/11506932.html
Copyright © 2020-2023  润新知