zoukankan      html  css  js  c++  java
  • BZOJ 3489 A simple rmq problem 可持久化KDtree/二维线段树

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3489

    题意概述:

    给出一个序列,每次询问一个序列区间中仅出现了一次的数字最大是多少,如果没有的话输出0。

    N<=100000,M<=200000.

    分析:

    考试的时候YY了一个可持久化KDtree可惜没有打完(一开始想着一维做最后发现自己真是太天真了hahahaha),最后把它改对了。

    把两种方法都介绍一下:

    持久化KDtree:

            首先我们令last[i]表示A[i]在i的左边最靠右的出现位置,那么显然对于询问[l,r],如果我们当前计算出的last是基于[1,r]的数据的,那么我们要找的实际上是(last[A[i]],i]中包含l的最大的A[i],转化一下就变成了last[A[i]]<l<=i,如果我们把它做成平面上的点(i,last[A[i]]),权值为A[i],并且序列中每个值仅有最后一次出现的位置对应的i做成的点有值,我们每一次询问[l,r]实际上询问的是基于[1,r]中的信息做成的点在平面范围{ (x,y) | x>=i,y<l }的点中的最大权值,这可以用KDtree来做。

            因此我们只要先把所有的点预处理出来建成KDtree,修改的时候可持久化一下就可以了。(如果您执意认为三维KDtree更好的话我只能说它太容易被卡啦......)

            期望时间复杂度O((N+M)sqrt(N))

    二维线段树:

            我们令Last[i]表示i左边第一个和i权值相同的点的下标(没有的话为0),Next[i]表示i右边第一个和i权值相同的点的下标(没有的话为N+1),那么对于询问[l,r]满足Last[i]<l<=i,i<=r<Next[i]来说,A[i]就可能成为它的解,可以发现我们把l当成平面的横坐标,r当成平面的纵坐标,i可以影响到的区域正好是平面上的一个矩形!因此只要用二维线段树动态开点+标记永久化就可以做了。

            时间复杂度O(M*logN^2)

            这两种做法的话,随机数据KDtree0.4s+吊打二维线段树2.8s+,而把询问构造成几乎全部接近于单点询问的时候KDtree到了极限大概3.6s的样子,这个时候二维线段树很开心1.8s+,三维KDtree就更不要说了5s+......(4s的时限干不死我的KDtreehahahahahaha如果您卡掉了我请不要怼我

            最后,我发现数据结构这种东西不同结构体写常数会小一些,尤其是当需要卡极限的时候不写在结构体里面效果就很显著......还有为什么把变量ans当成参数带到query里面去居然变快了?!是引用地址的原因吗......

            论常数优化的重要性!

    KDtree:

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<cstdlib>
      5 #include<algorithm>
      6 #include<cmath>
      7 #include<queue>
      8 #include<set>
      9 #include<map>
     10 #include<vector>
     11 #include<cctype>
     12 #define inf (1e8+5)
     13 using namespace std;
     14 const int MAXN=100005;
     15 const int maxn=5000005;
     16 const int maxm=100005;
     17 
     18 int N,M,A[MAXN],last[MAXN],cnt,D;
     19 struct XY{
     20     int d[2];
     21     friend bool operator < (XY a,XY b){
     22         return a.d[D]<b.d[D];
     23     }
     24     friend bool operator == (XY a,XY b){
     25         return a.d[0]==b.d[0]&&a.d[1]==b.d[1];
     26     }
     27 }p[MAXN],pp[MAXN],node[maxn],mn[maxn],mx[maxn];
     28 int root[maxm],np,lc[maxn],rc[maxn],maxv[maxn],w[maxn],ver[maxn];
     29 int copynode(int p,int t){
     30     np++,maxv[np]=maxv[p],w[np]=w[p];
     31     lc[np]=lc[p],rc[np]=rc[p],ver[np]=t;
     32     node[np]=node[p],mx[np]=mx[p],mn[np]=mn[p];
     33     return np;
     34 }
     35 void pushup1(int now){
     36     for(int i=0;i<2;i++){
     37         mn[now].d[i]=min(node[now].d[i],min(mn[lc[now]].d[i],mn[rc[now]].d[i]));
     38         mx[now].d[i]=max(node[now].d[i],max(mx[lc[now]].d[i],mx[rc[now]].d[i]));
     39     }
     40 }
     41 void pushup2(int now){
     42     maxv[now]=max(w[now],max(maxv[lc[now]],maxv[rc[now]]));
     43 }
     44 void build(int &now,int L,int R,XY *a,int k){
     45     if(L>R) return;
     46     now=++np,lc[now]=rc[now]=0;
     47     int m=L+R>>1; D=k;
     48     nth_element(a+L,a+m,a+R+1);
     49     node[now]=mx[now]=mn[now]=a[m];
     50     maxv[now]=w[now]=0;
     51     build(lc[now],L,m-1,a,k^1);
     52     build(rc[now],m+1,R,a,k^1);
     53     pushup1(now);
     54 }
     55 bool isoutside(XY p,int now){
     56     return p.d[0]<mn[now].d[0]||p.d[0]>mx[now].d[0]
     57         ||p.d[1]<mn[now].d[1]||p.d[1]>mx[now].d[1];
     58 }
     59 int update(int now,XY p,int v,int t){
     60     if(!now) return 0;
     61     if(isoutside(p,now)) return 0;
     62     if(node[now]==p){
     63         if(ver[now]!=t) now=copynode(now,t);
     64         w[now]=v; pushup2(now);
     65         return now;
     66     }
     67     int re=0;
     68     if(re=update(lc[now],p,v,t)){
     69         if(ver[now]!=t) now=copynode(now,t);
     70         lc[now]=re; pushup2(now);
     71         return now;
     72     }
     73     if(re=update(rc[now],p,v,t)){
     74         if(ver[now]!=t) now=copynode(now,t);
     75         rc[now]=re; pushup2(now);
     76         return now;
     77     }
     78     return 0;
     79 }
     80 void query(int now,int l,int &ret){
     81     if(mx[now].d[0]<l||mn[now].d[1]>=l) return;
     82     if(mn[now].d[0]>=l&&mx[now].d[1]<l){
     83         if(maxv[now]>ret) ret=maxv[now];
     84         return;
     85     }
     86     if(node[now].d[0]>=l&&node[now].d[1]<l&&w[now]>ret) ret=w[now];
     87     if(maxv[lc[now]]>ret) query(lc[now],l,ret);
     88     if(maxv[rc[now]]>ret) query(rc[now],l,ret);
     89 }
     90 
     91 void _scanf(int &x)
     92 {
     93     x=0;
     94     char ch=getchar();
     95     while(ch<'0'||ch>'9') ch=getchar();
     96     while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
     97 }
     98 int out_cnt; char out[15];
     99 void _printf(int x)
    100 {
    101     out[++out_cnt]=x%10+'0',x/=10;
    102     while(x) out[++out_cnt]=x%10+'0',x/=10;
    103     while(out_cnt) putchar(out[out_cnt--]);
    104     putchar('
    ');
    105 }
    106 void data_in()
    107 {
    108     _scanf(N);_scanf(M);
    109     for(int i=1;i<=N;i++) _scanf(A[i]);
    110     for(int i=1;i<=N;i++){
    111         p[i].d[0]=i,p[i].d[1]=last[A[i]];
    112         last[A[i]]=i;
    113     }
    114     for(int i=1;i<=N;i++) pp[i]=p[i];
    115     mx[0].d[0]=mx[0].d[1]=0,mn[0].d[0]=mn[0].d[1]=inf;
    116     build(root[0],1,N,p,0);
    117     for(int i=1;i<=N;i++){
    118         root[i]=root[i-1];
    119         if(pp[i].d[1]) root[i]=update(root[i],pp[pp[i].d[1]],0,i);
    120         root[i]=update(root[i],pp[i],A[i],i);
    121     }
    122 }
    123 void work()
    124 {
    125     int l,r,x,y,ans;
    126     for(int i=1;i<=M;i++){
    127         _scanf(x);_scanf(y);
    128         l=min((x+ans)%N+1,(y+ans)%N+1);
    129         r=max((x+ans)%N+1,(y+ans)%N+1);
    130         ans=0; query(root[r],l,ans);
    131         _printf(ans);
    132     }
    133 }
    134 int main()
    135 {
    136     data_in();
    137     work();
    138     return 0;
    139 }
    View Code

    二维线段树:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cstdlib>
     5 #include<algorithm>
     6 #include<cmath>
     7 #include<queue>
     8 #include<set>
     9 #include<map>
    10 #include<vector>
    11 #include<cctype>
    12 using namespace std;
    13 const int maxn=100005;
    14 const int maxnode=30000005;
    15 
    16 int N,M,a[maxn],Last[maxn],Next[maxn],last[maxn];
    17 int root,np,rt[maxnode],lc[maxnode],rc[maxnode],mx[maxnode];
    18 void inupdate(int &now,int L,int R,int A,int B,int v){
    19     if(!now) now=++np;
    20     if(A<=L&&R<=B){
    21         if(v>mx[now]) mx[now]=v;
    22         return;
    23     }
    24     int m=L+R>>1;
    25     if(B<=m) inupdate(lc[now],L,m,A,B,v);
    26     else if(A>m) inupdate(rc[now],m+1,R,A,B,v);
    27     else inupdate(lc[now],L,m,A,B,v),inupdate(rc[now],m+1,R,A,B,v);
    28 }
    29 int inquery(int now,int L,int R,int p){
    30     if(!now) return 0;
    31     if(L==R) return mx[now];
    32     int m=L+R>>1;
    33     if(p<=m) return max(mx[now],inquery(lc[now],L,m,p));
    34     return max(mx[now],inquery(rc[now],m+1,R,p));
    35 }
    36 void update(int &now,int L,int R,int A,int B,int AA,int BB,int v){
    37     if(!now) now=++np;
    38     if(A<=L&&R<=B){
    39         inupdate(rt[now],1,N,AA,BB,v);
    40         return;
    41     }
    42     int m=L+R>>1;
    43     if(B<=m) update(lc[now],L,m,A,B,AA,BB,v);
    44     else if(A>m) update(rc[now],m+1,R,A,B,AA,BB,v);
    45     else update(lc[now],L,m,A,B,AA,BB,v),update(rc[now],m+1,R,A,B,AA,BB,v);
    46 }
    47 int query(int now,int L,int R,int p1,int p2){
    48     if(!now) return 0;
    49     if(L==R) return inquery(rt[now],1,N,p2);
    50     int m=L+R>>1;
    51     int re=inquery(rt[now],1,N,p2);
    52     if(p1<=m) return max(re,query(lc[now],L,m,p1,p2));
    53     return max(re,query(rc[now],m+1,R,p1,p2));
    54 }
    55 
    56 void _scanf(int &x)
    57 {
    58     x=0;
    59     char ch=getchar();
    60     while(ch<'0'||ch>'9') ch=getchar();
    61     while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    62 }
    63 int out_cnt; char out[15];
    64 void _printf(int x)
    65 {
    66     out[++out_cnt]=x%10+'0',x/=10;
    67     while(x) out[++out_cnt]=x%10+'0',x/=10;
    68     while(out_cnt) putchar(out[out_cnt--]);
    69     putchar('
    ');
    70 }
    71 void data_in()
    72 {
    73     _scanf(N);_scanf(M);
    74     for(int i=1;i<=N;i++) _scanf(a[i]);
    75     for(int i=1;i<=N;i++)
    76         Last[i]=last[a[i]],last[a[i]]=i;
    77     for(int i=1;i<=N;i++) last[i]=N+1;
    78     for(int i=N;i>=1;i--)
    79         Next[i]=last[a[i]],last[a[i]]=i;
    80     for(int i=1;i<=N;i++)
    81         update(root,1,N,Last[i]+1,i,i,Next[i]-1,a[i]);
    82 }
    83 void work()
    84 {
    85     int x,y,l,r,ans=0;
    86     for(int i=1;i<=M;i++){
    87         _scanf(x);_scanf(y);
    88         l=min((x+ans)%N+1,(y+ans)%N+1);
    89         r=max((x+ans)%N+1,(y+ans)%N+1);
    90         ans=query(root,1,N,l,r);
    91         _printf(ans);
    92     }
    93 }
    94 int main()
    95 {
    96     data_in();
    97     work();
    98     return 0;
    99 }
    View Code
  • 相关阅读:
    Less 文档查看心得
    Jquery+SlideDown+在IE7和IE6中的bug
    Highcharts 图表库
    安卓 日常问题 工作日志6
    安卓 日常问题 工作日志5
    安卓 日常问题 工作日志 3
    安卓 日常问题 工作日志 2
    安卓 日常问题 工作日志
    新的开始 安卓工程师
    2018.4.16号 我也不知道应该写点什么
  • 原文地址:https://www.cnblogs.com/KKKorange/p/8783403.html
Copyright © 2011-2022 走看看