• CF1004F Sonya and Bitwise OR(线段树平衡复杂度+or 前缀性质)


    CF1004F Sonya and Bitwise OR

    有一个长度为 n" role="presentation">n 的数组 {a}" role="presentation">{a},有 m" role="presentation">m 次操作,又给定一个数 x" role="presentation">x,有两类操作:

    • 1 i yai" role="presentation">ai 改为 y" role="presentation">y
    • 2 l r 查询有多少个区间 [L,R]" role="presentation">[L,R] 满足 aLR" role="presentation">aLR 的按位或 or" role="presentation">or 大于等于 x" role="presentation">x

    1n,m105;0ai,x,y220" role="presentation">1n,m105;0ai,x,y220

    先考虑如果只有询问操作怎么做。

    那可以算出这段区间的中点开始,算出 [l,m]" role="presentation">[l,m] 的后缀 or" role="presentation">or[m+1,r]" role="presentation">[m+1,r] 的前缀 or" role="presentation">or

    那么可以通过双指针来求出每一个符合条件的区间,如果改为多次询问就用树状数组记下来,一次 logn" role="presentation">logn 查询,总复杂度 nlogn" role="presentation">nlogn

    Hint" role="presentation">Hint:到这一步之后就一直在想用 CDQ 分治来完成对询问的处理,但其实因为是单点修改,多次询问,可以思考用线段树维护这一过程。而且线段树自带分治功能。

    Trick" role="presentation" style="position: relative;">Trick:一个非常重要的性质,在一段数的 or" role="presentation" style="position: relative;">or 前缀中,只可能出现 log2a" role="presentation" style="position: relative;">log2a 个可能的值。每次最多让一个 0" role="presentation" style="position: relative;">0 位变成 1" role="presentation" style="position: relative;">1,因此最多只会有 log2a" role="presentation" style="position: relative;">log2a 段。

    那么可以在线段树中维护区间内的前后缀 or" role="presentation" style="position: relative;">or 段,每个区间都不会超过 20" role="presentation" style="position: relative;">20 个,同时记录这个区间内的合法子区间数量,像线段树一样修改询问即可。

    总结:

    最开始的 CDQ 想法大体方向是正确的,和正解都利用了分治。但是存在修改操作后,对区间分治就不能够在 CDQ 上进行,必须另寻他路。

    那么接下来就应当考虑在线解决这个问题,这道题的区间是可以合并的,而平衡复杂度的思想就让我们想到了线段树

    最后这个经典的 Trick" role="presentation" style="position: relative;">Trick 我不能够熟练使用,下次就知道了。

    1. #define Maxn 100005
    2. int n,m,X;
    3. int a[Maxn];
    4. struct SUB
    5. {
    6. SUB(int Val=0,int Pl=0,int Pr=0):val(Val),pl(Pl),pr(Pr){}
    7. int val,pl,pr;
    8. };
    9. SUB tpre[100],tsuf[100];
    10. struct TREE
    11. {
    12. ll sum;
    13. vector pre,suf;
    14. TREE(){ sum=0,pre.clear(),suf.clear(); }
    15. inline void push(int x,int Nu)
    16. {
    17. pre.clear(),suf.clear(),sum=(x>=X),
    18. pre.pb(SUB(x,Nu,Nu)),suf.pb(SUB(x,Nu,Nu));
    19. }
    20. }tree[Maxn<<2];
    21. inline TREE merge(TREE L,TREE R)
    22. {
    23. int ppre=0,psuf=0,tl=L.pre.back().val,tr=R.suf.front().val;
    24. for(SUB v:L.pre) tpre[++ppre]=v;
    25. for(SUB v:R.pre) tpre[++ppre]=SUB(v.val|tl,v.pl,v.pr);
    26. for(SUB v:L.suf) tsuf[++psuf]=SUB(v.val|tr,v.pl,v.pr);
    27. for(SUB v:R.suf) tsuf[++psuf]=v;
    28. tpre[ppre+1]=SUB(-1,0,0),tsuf[psuf+1]=SUB(-1,0,0);
    29. TREE ret; ret.sum=L.sum+R.sum;
    30. for(int nl=0,nr=0,sl=L.suf.size()-1,sr=R.pre.size()-1;nl<=sl;nl++)
    31. {
    32. while(nr<=sr && (L.suf[nl].val|R.pre[nr].val)
    33. if(nr>sr) break;
    34. ret.sum+=1ll*(R.pre[sr].pr-R.pre[nr].pl+1)*
    35. (L.suf[nl].pr-L.suf[nl].pl+1);
    36. }
    37. for(int i=2,Last=0;i<=ppre+1;i++) if(tpre[i].val!=tpre[i-1].val)
    38. assert(tpre[Last+1].val==tpre[i-1].val),
    39. ret.pre.pb(SUB(tpre[i-1].val,tpre[Last+1].pl,tpre[i-1].pr)),Last=i-1;
    40. for(int i=2,Last=0;i<=psuf+1;i++) if(tsuf[i].val!=tsuf[i-1].val)
    41. assert(tsuf[Last+1].val==tsuf[i-1].val),
    42. ret.suf.pb(SUB(tsuf[i-1].val,tsuf[Last+1].pl,tsuf[i-1].pr)),Last=i-1;
    43. assert(ret.pre.size()<=30 && ret.suf.size()<=30);
    44. return ret;
    45. }
    46. void build(int p,int nl,int nr)
    47. {
    48. if(nl==nr) { tree[p].push(a[nl],nl); return; }
    49. int mid=(nl+nr)>>1;
    50. build(p<<1,nl,mid),build(p<<1|1,mid+1,nr);
    51. tree[p]=merge(tree[p<<1],tree[p<<1|1]);
    52. }
    53. void change(int p,int nl,int nr,int x,int k)
    54. {
    55. if(nl==nr) { tree[p].push(k,x); return; }
    56. int mid=(nl+nr)>>1;
    57. if(mid>=x) change(p<<1,nl,mid,x,k);
    58. else change(p<<1|1,mid+1,nr,x,k);
    59. tree[p]=merge(tree[p<<1],tree[p<<1|1]);
    60. }
    61. TREE query(int p,int nl,int nr,int l,int r)
    62. {
    63. if(nl>=l && nr<=r) return tree[p];
    64. int mid=(nl+nr)>>1;
    65. if(mid>=l && mid
    66. return merge(query(p<<1,nl,mid,l,r),query(p<<1|1,mid+1,nr,l,r));
    67. else if(mid>=l) return query(p<<1,nl,mid,l,r);
    68. else return query(p<<1|1,mid+1,nr,l,r);
    69. }
    70. int main()
    71. {
    72. n=rd(),m=rd(),X=rd();
    73. for(int i=1;i<=n;i++) a[i]=rd();
    74. build(1,1,n);
    75. for(int i=1,opt,x,y;i<=m;i++)
    76. {
    77. opt=rd(),x=rd(),y=rd();
    78. if(opt==1) change(1,1,n,x,y);
    79. else printf("%lld\n",query(1,1,n,x,y).sum);
    80. }
    81. return 0;
    82. }
  • 相关阅读:
    <dependencyManagement>的作用
    k8s-19 资源限制与监控
    支撑Java NIO 与 NodeJS的底层技术
    学习阶段单片机买esp32还是stm32?
    Maven Maven的概述
    通讯网关软件008——利用CommGate X2Mysql实现OPC数据转储Mysql
    lamba stream处理集合
    内网穿透方法汇总
    【数据结构】图—图的邻接矩阵存储及度计算
    Python学习七:数据库编程接口
  • 原文地址:https://blog.csdn.net/qq_53299575/article/details/126882516