• CF474E 【Pillars】题解


    算法分析:

    首先看到题目给的条件,可以想到求最长上升子序列的 O(n \log n) 做法(本人习惯用树状数组)。

    f_i 表示以 i 结尾的符合题目要求的子序列的最大长度,一个 O(n^2) 的做法是 f[i] = max(f[j]+1),其中需要满足 j<i 并且 \mid a[i]-a[j] \mid \geq d

    但这道题的数据范围,是需要用 O(n \log n) 的做法来通过。

    由于对于一个位置 i,能被转移到的位置是有限的,必须满足 \mid a[i]-a[j] \mid \geq d,把绝对值打开可以得到 a[i] \geq a[j]+d 或 a[i] \leq a[j]-d,可以发现被能转移到的区间被分为了两部分,1 到 a[j]-d 和 a[j]+d 到给定的上界。所以我们可以维护一个线段树来记录区间最大值。

    这时我们的 f_i​ 可以通过线段树求两个区间的最大值的最大值 +1 来转移。

    上述是查询操作,接下来看修改操作。已知当前点 i,把 a_i​ 作为下标,f_i 作为值来进行修改,把影响到的节点的区间最大值进行比较并修改。

    最后一步,考虑如何统计答案。由于让我们输出最长子序列的下标,我们可以另外用一个数组 pre_i,来记录当前点的 f_i​ 值是由前面哪一个点转移过来的。然后一直跳 pre 数组,用一个栈记录,最后把栈中的元素输出即可。

    注意!这道题的数据范围不能直接传统线段树,需要动态开点。

    总代码:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #define re register
    8. #define int long long
    9. #define drep(a,b,c) for(re int a(b) ; a>=(c) ; --a)
    10. #define rep(a,b,c) for(re int a(b) ; a<=(c) ; ++a)
    11. using namespace std;
    12. inline int read(){
    13. int x=0,f=1;char ch=getchar();
    14. while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
    15. while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    16. return x*f;
    17. }
    18. const int M = 1e5+10;
    19. const int maxn = 1e15;
    20. int n,d,root,cnt,ans,anspos;
    21. int a[M],f[M],pre[M];
    22. struct tree{
    23. int ls,rs,id,val;
    24. }t[M*40];
    25. #define ls t[k].ls
    26. #define rs t[k].rs
    27. inline void pushup(int k){ //更新节点信息
    28. if(t[ls].val > t[rs].val) t[k].val = t[ls].val,t[k].id = t[ls].id;
    29. else t[k].val = t[rs].val,t[k].id = t[rs].id;
    30. }
    31. inline void modify(int &k,int l,int r,int x,int val,int id){
    32. if(!k) k=++cnt; //需要动态开点
    33. if(l == r){
    34. if(val > t[k].val) t[k].val = val,t[k].id = id; //更新
    35. return;
    36. }
    37. int mid = (l+r)>>1;
    38. if(x<=mid) modify(ls,l,mid,x,val,id);
    39. else modify(rs,mid+1,r,x,val,id);
    40. pushup(k); //常规操作
    41. }
    42. inline tree query(int k,int l,int r,int x,int y){
    43. if(!k || yreturn (tree){0,0,0,0};
    44. if(x<=l && r<=y) return t[k];
    45. int mid = (l+r)>>1;
    46. tree res1 = (tree){0,0,0,0},res2 = (tree){0,0,0,0};
    47. if(x<=mid) res1 = query(ls,l,mid,x,y);
    48. if(y>mid) res2 = query(rs,mid+1,r,x,y);
    49. if(res1.val < res2.val) res1.val = res2.val,res1.id = res2.id; //需要找到最大值
    50. return res1;
    51. }
    52. signed main(){
    53. n=read(),d=read();
    54. rep(i,1,n) a[i] = read();
    55. rep(i,1,n){
    56. tree tmp1 = query(root,1,maxn,1,a[i]-d);
    57. tree tmp2 = query(root,1,maxn,a[i]+d,maxn); //注意,是两个区间,要取这两个区间的最大值
    58. if(tmp1.val < tmp2.val) swap(tmp1,tmp2); //把tmp1记为答案
    59. f[i] = tmp1.val + 1; //进行dp修改
    60. pre[i] = tmp1.id; //标转移到i的位置
    61. if(ans < f[i]) ans=f[i],anspos=i; //最后输出的答案
    62. modify(root,1,maxn,a[i],f[i],i); //需要把a[i]当成下标,f[i]当成值进行修改
    63. }
    64. printf("%lld\n",ans);
    65. stack<int> s; //如上述所说,记录答案
    66. for(re int i(anspos) ; i ; i=pre[i]) s.push(i); //一直跳pre就行
    67. while(!s.empty()) printf("%lld ",s.top()),s.pop();
    68. return 0;
    69. }
  • 相关阅读:
    lambda表达式,for_each、find_if简介
    pytorch的axis的理解
    2022年美团春招真题:字符串排序
    STM32实战总结:HAL之DMA
    Ros Cmakelist 编译配置
    设计模式之美——封装,继承,多态的意义
    TP5的消息队列
    iptables防火墙
    CJ20N 项目定义属性字段增强
    【最简便方法】element-plus/element-ui走马灯配置图片以及图片自适应
  • 原文地址:https://blog.csdn.net/glorious_dream/article/details/126310645