zoukankan      html  css  js  c++  java
  • 前缀和线性基HDU6579

    Operation

     题解:看到区间最大异或和,首先想到的是线性基;(读题发现要用到上一次的结果,也就是要强制在线,然后自己刚学完主席树就想是不是主席树套线性基,但是这是会超时的)

    线性基可以处理的操作是:

    • 在数列末尾插入一个数
    • 查询全局的子集异或最大值

    由于线性基的长度很短,因此我们可以将数列所有前缀的线性基保存下来。1到x的线性基可以由1到x-1的线性基通过插入a[x]来求得,这样,我们就可以查询前缀区间的子集异或最大值。现在问题的关键在于,查询区间 [L, R] 时,如何避免 [1, L-1] 的干扰。

    考虑线性基的插入过程,如果线性基当前位上已经有值,我们就不能把待插入的值放入这一位,因此线性基上每一位的数,都是对应位上在原数列最左侧的数字。现在我们改变策略,使得线性基上每一位的数,都变成对应位上在原数列最右侧的数字。实现这个策略的方法是:我们额外保存线性基上每一位数在原数列中的位置,插入的时候,如果对应位上的数在原数列中更靠左,就用待插入的数和它交换。基于这种策略,我们在查询区间 [L, R] 时,可以在区间 [1, R] 对应的线性基中查询,对于线性基上每一位的数,如果它在原数组中出现的位置比 L 更靠右,就考虑它对答案的贡献,否则直接跳过这一位。

    这个做法的正确性也很显然,通过改变策略,使线性基上每一位数变成对应位上在原数列最右侧的数字,可以看成线性基插入数字的顺序变反,完全不影响线性基的性质。同时,将线性基上所有在原数组中的位置比 x 更靠左的数字删除,可以视为区间 [1, L-1] 的数字还没有被插入线性基。

    复杂度:O((n + m) logx),n为初始数列长度,m为操作次数,x为值域大小。

    大佬的博客讲解:here

    AC_Code:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 using namespace std;
     5 typedef long long ll;
     6 #define endl '
    '
     7 const int maxn = 35;
     8 const int maxm = 5e5+5;
     9 const int inf = 0x3f3f3f3f;
    10 
    11 int cnt;//当前已插入的数的个数
    12 int a[maxm][maxn];//保存所有前缀区间的线性基
    13 int b[maxm][maxn];//保存线性基上的数字在原数组上的对应位置
    14 int n,m;
    15 
    16 void LB(int x){
    17     int cur=++cnt;//表示待插入的数字在原数组上的位置
    18     for(int i=31;i>=0;i--){
    19         a[cnt][i]=a[cnt-1][i];
    20         b[cnt][i]=b[cnt-1][i];
    21     }
    22     for(int i=31;i>=0;i--){
    23         if( !(x>>i) ) continue;
    24         if( !a[cnt][i] ){
    25             a[cnt][i]=x;
    26             b[cnt][i]=cur;
    27             break;
    28         }
    29         else{
    30             if( cur>b[cnt][i] ){    //如果待插入的数字在原数组上更靠右,则用线性基上的数与其交换
    31                 swap(a[cnt][i],x);
    32                 swap(b[cnt][i],cur);    //位置也要交换
    33             }
    34             x^=a[cnt][i];
    35         }
    36     }
    37 }
    38 
    39 int query(int l,int r){
    40     l=l%cnt+1; r=r%cnt+1;   //注意这里是%cnt,不是%n
    41     if( l>r ) swap(l,r);
    42     int ret=0;
    43     for(int i=31;i>=0;i--){
    44         if( b[r][i]>=l ){   //如果在原数组中的位置比l更靠右,那么就产生贡献,此处b[r][i]就已经限制了右区间
    45             ret=max(ret,ret^a[r][i]);   //线性基贪心求最大值的基本操作
    46         }
    47     }
    48     return ret;
    49 }
    50 
    51 
    52 
    53 int main()
    54 {
    55     int t; scanf("%d",&t);
    56     while( t-- ){
    57         cnt=0;
    58         scanf("%d%d",&n,&m);
    59         for(int i=0;i<n;i++){
    60             int a;
    61             scanf("%d",&a);
    62             LB(a);
    63         }
    64         int lastans=0, opt, x, y;   //lastans用于处理强制在线
    65         for(int i=0;i<m;i++){
    66             scanf("%d%d",&opt,&x);
    67             if( opt==0 ){
    68                 scanf("%d",&y);
    69                 x=x^lastans;
    70                 y=y^lastans;
    71                 lastans = query(x, y);
    72                 printf("%d
    ",lastans);
    73             }
    74             else{
    75                 LB(x^lastans);
    76             }
    77         }
    78     }
    79     return 0;
    80 }
  • 相关阅读:
    递归要记得返回
    git push的时候报Unable to find remote helper for 'https'的错误
    iphone手机上的click和touch事件
    对jquery的conflict方法的解读
    开发富文本编辑器的一些体会
    使用jquery制作动态加载型菜单
    解决jquery$命名符和其它框架的冲突问题
    基于geolocation来获取经纬度地址
    Python学习笔记2解析数据
    Python学习笔记1数据类型
  • 原文地址:https://www.cnblogs.com/wsy107316/p/13364886.html
Copyright © 2011-2022 走看看