zoukankan      html  css  js  c++  java
  • 洛谷P3928 Sequence2(dp,线段树)

    题目链接:

    洛谷

    题目大意在描述底下有。此处不赘述。


    明显是个类似于LIS的dp。

    令 $dp[i][j]$ 表示:

    • $j=1$ 时表示已经处理了 $i$ 个数,上一个选的数来自序列 $A[0]$ 的最长长度
    • $j=2$ 表示 $A[1]$
    • $j=3$ 表示 $A[2]$ 且是单调递减
    • $j=4$ 表示 $A[2]$ 且是单调递增

    (为了方便,我们令 $seq[x]$ 表示当上文中的 $j=x$ 时表示哪个序列)

    那么有转移方程:

    • $dp[i][1]=maxlimits_{1le j<i,kin{1,2,3,4}}{dp[j][k]:A[seq[k]][j]le A[0][i]}+1$(从前面任选一个更小的)
    • $dp[i][2]=maxlimits_{1le j<i,kin{1,2,3,4}}{dp[j][k]:A[seq[k]][j]ge A[0][i]}+1$(同理)
    • $dp[i][3]=maxlimits_{1le j<i,kin{1,2,3}}{dp[j][k]:A[seq[k]][j]le A[0][i]}+1$(因为第三个序列连续一段状态应相同,所以不能从4转移)
    • $dp[i][4]=maxlimits_{1le j<i,kin{1,2,4}}{dp[j][k]:A[seq[k]][j]ge A[0][i]}+1$(同上)

    可以朴素dp,复杂度 $O(n^2)$。

    优化?这个真的很像LIS,那么就用二分或者树状数组/线段树吧。

    二分我不会……(当然二分可能做不了)

    树状数组懒得打后缀最大了……

    那就线段树吧,跟普通的LIS类似,开4棵线段树,分别维护 $j=1,2,3,4$。每次求一下前缀和后缀最大,然后插进线段树中即可。

    (其实是可以只开3棵线段树的,$j=1,2$ 可以合在一起。但是我从前写的时候出锅了,以为是3棵线段树的锅,改成了4棵,后来发现不是这里出bug……懒得改了)

    当然 $mle 10^9$,需要离散化。

    (注:离散化后最大的数可达 $3n$,所以要开4棵大小为 $3n$ 的线段树)

    时间复杂度是 $O(nlog n)$。


    代码+注释:(代码长得有点丑)

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int maxn=100010;
     4 int n,a[maxn],b[maxn],c[maxn],tmp[maxn*3],sz,ans=1;    //tmp是离散化用到的数组,sz是离散化后所有数的最大值,可以算是常数优化
     5 int cnt,maxv[maxn*25],ch[maxn*25][2];    //我用的动态开点线段树,一棵树节点数为约6n,而有4棵树
     6 inline int lwrbnd(int x){    //离散化
     7     return lower_bound(tmp+1,tmp+sz+1,x)-tmp;
     8 }
     9 struct segment{    //线段树维护区间最大
    10     int n,root;
    11     inline void pushup(int x){
    12         maxv[x]=max(maxv[ch[x][0]],maxv[ch[x][1]]);
    13     }
    14     void build(int &x,int l,int r){
    15         x=++cnt;
    16         if(l==r) return;
    17         int mid=l+r>>1;
    18         build(ch[x][0],l,mid);
    19         build(ch[x][1],mid+1,r);
    20     }
    21     segment(){}
    22     segment(int n_):n(n_){build(root,1,n);}
    23     void update_(int x,int l,int r,int p,int k){
    24         if(l==r) return void(maxv[x]=max(maxv[x],k));    //这里不能直接修改,如果之前相同位置的dp值更大则要保留
    25         int mid=l+r>>1;
    26         if(mid>=p) update_(ch[x][0],l,mid,p,k);
    27         else update_(ch[x][1],mid+1,r,p,k);
    28         pushup(x);
    29     }
    30     void update(int p,int k){
    31         update_(root,1,n,p,k);
    32     }
    33     int query_(int x,int L,int R,int l,int r){
    34         if(L>=l && R<=r) return maxv[x];
    35         int mid=L+R>>1,ans=0;
    36         if(mid>=l) ans=max(ans,query_(ch[x][0],L,mid,l,r));
    37         if(mid<r) ans=max(ans,query_(ch[x][1],mid+1,R,l,r));
    38         return ans; 
    39     }
    40     int query(int l,int r){
    41         return query_(root,1,n,l,r);
    42     }
    43 }sgm1,sgm2,sgm3,sgm4;    //4棵线段树
    44 int main(){
    45     scanf("%d",&n);
    46     for(int i=1;i<=n;i++) scanf("%d",a+i),tmp[i]=a[i];
    47     for(int i=1;i<=n;i++) scanf("%d",b+i),tmp[i+n]=b[i];
    48     for(int i=1;i<=n;i++) scanf("%d",c+i),tmp[i+2*n]=c[i];
    49     sort(tmp+1,tmp+3*n+1);
    50     sz=unique(tmp+1,tmp+3*n+1)-tmp-1;
    51     sgm1=segment(sz);sgm2=segment(sz);sgm3=segment(sz);sgm4=segment(sz);    //建树(我的代码这里不能连等,否则4棵线段树实际的维护值一样)
    52     for(int i=1;i<=n;i++) a[i]=lwrbnd(a[i]),b[i]=lwrbnd(b[i]),c[i]=lwrbnd(c[i]);    //离散化
    53     for(int i=1;i<=n;i++){
    54         int tmp1=max(sgm1.query(1,a[i]),max(sgm2.query(1,a[i]),max(sgm3.query(1,a[i]),sgm4.query(1,a[i]))))+1;
    55         int tmp2=max(sgm1.query(b[i],sz),max(sgm2.query(b[i],sz),max(sgm3.query(b[i],sz),sgm4.query(b[i],sz))))+1;
    56         int tmp3=max(sgm1.query(1,c[i]),max(sgm2.query(1,c[i]),sgm3.query(1,c[i])))+1;
    57         int tmp4=max(sgm1.query(c[i],sz),max(sgm2.query(c[i],sz),sgm4.query(c[i],sz)))+1;
    58         //上面四行是从线段树中查询前缀/后缀最大值并且更新
    59         ans=max(ans,max(tmp1,max(tmp2,max(tmp3,tmp4))));
    60         sgm1.update(a[i],tmp1);sgm2.update(b[i],tmp2);sgm3.update(c[i],tmp3);sgm4.update(c[i],tmp4);    //插入线段树
    61     }
    62     printf("%d
    ",ans);
    63 }
    线段树优化dp
  • 相关阅读:
    java-5
    java-4
    java-03
    java-02
    Java的集合类
    数据库之约束
    网络编程
    多表查询
    二维数组打印乘法表,三角形,输入三个数,输出最大值
    例:进店买衣服案例
  • 原文地址:https://www.cnblogs.com/1000Suns/p/9560050.html
Copyright © 2011-2022 走看看