zoukankan      html  css  js  c++  java
  • 【xsy1098】第k小 可持久化trie

    题目大意:你要维护一个长度为$n$的序列,资瓷对整个序列$xor,and,or$一个数,以及区间第k小查询。

    数据范围:$n≤50000$,所有数字$<2^{31}$。

    此题甚妙

    我们不难想出没有位运算的区间第k大查询,直接可持久化trie就可以了。

    考虑此题只有xor操作。

    我们记一个$last$表示之前所有异或操作的数的异或和。

    查询区间第$k$小时,随便查询下就行了(在比较时假装交换$trie$的左右子树)

    考虑存在$or$和$and$操作,我们设对序列操作的数字为$x$,设$vis[k]$表示整个序列的第$k$位是否被全部归零过。

    不难发现,$and$操作对第i位有意义,当且仅当数字$x$的第$i$位为$0$,($or$情况类似等下会讲),在这种情况下,对于序列上的所有数,第$i$位都会归零,(last的第$i$位也会归零)

    同理,$or$操作对第i位有意义,当且仅当数字$x$的第$i$位为$1$,对于序列上所有数,第$i$位都会变为1($last$的第$i$位也会变为1)

    实际上两个操作本质上是相同的,我们可以用$and$操作和$last|=x$去实现$or$操作。

    不难发现,对于数列的每一位,归零操作至多只需要执行一次。

    那么,对于每个需要执行归零操作的$or$或$and$操作,我们重建一次可持久化$trie$就可以了。

    时间复杂度$O(n log^2 V+m log V) $其中V为数列中的最大值。

     1 #include<bits/stdc++.h>
     2 #define M 50005
     3 #define INF 2147483647
     4 using namespace std;
     5 
     6 struct trie{int a[2],siz;}a[M*33]={0}; int use=0;
     7 void add(int &x,int dep,int zhi){
     8     a[++use]=a[x]; a[x=use].siz++;
     9     if(dep==-1) return;
    10     bool k=zhi&(1<<dep);
    11     if(k) add(a[x].a[1],dep-1,zhi);
    12     else add(a[x].a[0],dep-1,zhi);
    13 }    
    14 int num[M]={0},n,m,zhi=0,root[M]={0},vis[32]={0},pls=INF;
    15 
    16 int query(int x,int y,int dep,int K){
    17     if(dep==-1) return 0;
    18     bool k=zhi&(1<<dep);
    19     int now=a[a[y].a[k]].siz-a[a[x].a[k]].siz,res=1<<dep;
    20     if(K<=now) res=query(a[x].a[k],a[y].a[k],dep-1,K);
    21     else res+=query(a[x].a[k^1],a[y].a[k^1],dep-1,K-now);
    22     return res;
    23 }
    24 
    25 void build(){
    26     for(int i=1;i<=n;i++) num[i]=num[i]&pls;
    27     use=0;
    28     for(int i=1;i<=n;i++){
    29         root[i]=root[i-1];
    30         add(root[i],30,num[i]);
    31     }
    32 }
    33 int main(){
    34     scanf("%d%d",&n,&m);
    35     for(int i=1;i<=n;i++) scanf("%d",num+i);
    36     build();
    37     while(m--){
    38         char op[4]; int x,l,r,k; pls=INF;
    39         scanf("%s",op);
    40         if(op[0]=='X'){scanf("%d",&x); zhi^=x;}
    41         if(op[0]=='O'){
    42             scanf("%d",&x);
    43             for(int i=0;i<=30;i++)
    44             if(((1<<i)&x)){
    45                 if(vis[i]==0) vis[i]=1,pls^=(1<<i);
    46                 zhi|=(1<<i); 
    47             }
    48             if(pls==INF) continue;
    49             build();
    50         }
    51         if(op[1]=='n'){
    52             scanf("%d",&x);
    53             for(int i=0;i<=30;i++)
    54             if(((1<<i)&x)==0){
    55                 if(vis[i]==0) vis[i]=1,pls^=(1<<i);
    56                 zhi&=(INF-(1<<i)); 
    57             }
    58             if(pls==INF) continue;
    59             build();
    60         }
    61         if(op[1]=='s'){
    62             scanf("%d%d%d",&l,&r,&k);
    63             printf("%d
    ",query(root[l-1],root[r],30,k));
    64         }
    65     }
    66 }
  • 相关阅读:
    C#中实现简单的预警提示功能(语音提示与弹窗提示)
    C#中使用SoundPlayer播放音频文件(wav文件)
    Angular中路由的嵌套-父子路由
    Winform中设置ZedGraph的坐标轴的标题和刻度不显示十次幂
    Angular中使用JS实现路由跳转、动态路由传值、get方式传值
    surprise库官方文档分析(二):使用预测算法
    surprise库官方文档分析(一)
    webpack官方文档分析(三):Entry Points详解
    webpack官方文档分析(二):概念
    webpack官方文档分析(一):安装
  • 原文地址:https://www.cnblogs.com/xiefengze1/p/10352120.html
Copyright © 2011-2022 走看看