区间子集最大/最小异或和问题(线性基,树上差分)

本文探讨了线性基在处理序列元素异或和问题中的应用,特别是在面对大规模数据时的优化策略。通过分析序列的特性,提出了一种高效算法,能够在(n+q)k的时间复杂度下解决区间异或和的最大化问题,显著提升了处理效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

闲话

有这样一个问题——一个长度为\(n\)的序列\(a_1-a_n\)\(q\)个询问,每次询问\(l,r\),选出\(\{a_l,a_{l+1}...a_{r}\}\)中一个子集使得子集内元素异或和最大/小。

第一次出现在HNOI模拟赛,当时的\(n,q\)只有大概\(3*10^4\)还是\(10^5\)的样子。然后毫不犹豫的写了个\(n\log^3n+q\log^2n\)的线性基ST表过了。。。

第二次出现在NOI模拟赛,数据范围大到了\(10^6\)!!!因为上一次IOI金牌爷laofu踩了标算。。。然后发现自己上次没听懂,只会写个两个\(\log\)的分治。

第三次出现在NOI.AC的NOIP lus模拟赛,然后发现自己还是没听懂。。。。。。

题目

NOIAC41 最短路

是个巧(wu)妙(chi)的二合一,另一部分的思路来自洛谷P4151 [WC2011]最大XOR和路径

注意到线性基的一些特性:当向其中插入若干个位长为\(k\)的整数后,实际上真正插入到其中的只有\(k\)个数。也就是说,对于一个区间\([l,r]\),当我们从\(r\)\(l\)依次尝试插入数的过程中,最多会产生\(k\)个本质不同的线性基,而且这些数在线性基内的插入行两两不同。

我们离线处理,对于右端点都在\(r\)的询问区间一起考虑。我们已知\([1,r]\)的线性基每一行被插入的数在原序列中的位置(从\(r\)\(l\)插入)。对于一个询问,我们取出线性基中所有被插入位置\(\ge l\)的行来更新答案。

那么如何快速从\([1,r-1]\)的线性基变到\([1,r]\)呢?毫无疑问我们这时会先插入\(a_r\),设它插入到了第\(j\)行。那么原来在第\(j\)行的数现在就不会留在这里了,会\(xor\ a_r\)后继续尝试着插入下面的行。操作过程就是:把原来的数取出来,插入当前数,继续把后面原来的数取出来。。。如是循环。

总的复杂度变成了\((n+q)k\),十分优秀。

#include<bits/stdc++.h>
#define RG register
#define R RG int
#define G if(++ip==iend)fread(ip=buf,1,N,stdin)
using namespace std;
const int N=3e5+9,M=6e5+9;
char buf[N],*iend=buf+N,*ip=iend-1;
int he[N],ne[M],to[M],w[M],s[N],a[N],b[N],l[N],lb[39],at[39];
bool vis[N];
inline int in(){
    G;while(*ip<'-')G;
    R x=*ip&15;G;
    while(*ip>'-'){x*=10;x+=*ip&15;G;}
    return x;
}
inline void chkmn(R&x,R y){
    if(x>y)x=y;
}
void dfs(R x){
    vis[x]=1;
    for(R y,i=he[x];i;i=ne[i])
        if(!vis[y=to[i]])s[y]=s[x]^w[i],dfs(y);
}
int main(){
    R n=in(),m=in(),q=in(),p=0,i,r,x,y;
    for(i=1;i<n;++i){
        x=in();y=in();
        ne[++p]=he[x];to[he[x]=p]=y;
        ne[++p]=he[y];to[he[y]=p]=x;
        w[p]=w[p-1]=in();
    }
    dfs(1);
    for(i=1;i<=m;++i)
        a[i]=s[in()]^s[in()]^in();
    memset(he+1,0,n<<2);
    for(i=1;i<=q;++i){
        b[i]=s[in()]^s[in()];
        l[i]=in();ne[i]=he[r=in()];he[r]=i;
    }
    for(r=1;r<=m;++r){
        x=a[r];p=r;
        for(i=30;~i;--i)//更新线性基
            if(1<<i&x){
                if(!lb[i]){
                    lb[i]=x;at[i]=p;
                    break;
                }
                if(at[i]<p)
                    swap(lb[i],x),swap(at[i],p);
                x^=lb[i];
            }
        for(x=he[r];x;x=ne[x])//求答案
            for(i=30;~i;--i)
                if(at[i]>=l[x])chkmn(b[x],b[x]^lb[i]);
    }
    for(i=1;i<=q;++i)
        printf("%d\n",b[i]);
    return 0;
}

转载于:https://www.cnblogs.com/flashhu/p/9670045.html

### 线性基在求最大异或中的作用 #### 异或运算原理 异或运算是二进制位上的逻辑运算,遵循以下规则:两个相同比特的结果为 `0`,不同则为 `1`。例如,`1 XOR 1 = 0` `1 XOR 0 = 1`。由于其特殊的性质——交换律、结合律以及自反性(任何数与自己异或结果为 `0`),它成为许多算法问题的核心工具之一[^2]。 #### 线性基的作用 线性基是一种能够有效处理一组整数间相互关系的数据结构,尤其适用于涉及异或操作的问题。它的主要特性如下: - 它是一个最小化的集合,其中每个元素都无法通过其余成员的异或组合生成。 - 这种独特属性确保了原集中所有可能产生的异或值都可以由线性基内的有限数量项重新构建出来[^1]。 这些特点使线性基非常适合用来解答关于最大化或者寻找特定条件下最优解等问题,比如本案例中的“最大异或”。 #### 使用原因分析 为了获得最大异或,我们需要考虑的是如何挑选合适的子集使得最终得到的整体效果最佳化。然而直接枚举所有的可能性通常是不可行的因为时间复杂度太高(O(2^n))。此时引入线性基就显得尤为重要因为它提供了以下几个优势: 1. **减少候选数目**: 利用线性独立原则筛选掉那些可以通过已有选定向量合成的新向量从而大大降低搜索空间大小; 2. **保持原有价值范围不变**: 即便进行了压缩转换过程仍能保证原来数值体系所能表达的一切潜在结果得以保留下来[^3]; 3. **便于查找极值点**: 当完成了上述准备工作之后就可以很方便地依据高位优先的原则逐步试探直至定位全局最优点为止[^4]. 综上所述,借助于线性基不仅可以显著提升效率而且还能保障准确性因此成为了此类挑战的理想解决方案。 ```python class LinearBasis: def __init__(self): self.basis = [0]*64 def insert(self,x): for i in reversed(range(64)): if x&(1<<i): if not self.basis[i]: self.basis[i]=x return True else: x^=self.basis[i] return False def get_max_xor(self): res=0 for b in self.basis[::-1]: if(res^b)>res: res=res^b return res # Example Usage lb = LinearBasis() for val in [9,8,7,6]: lb.insert(val) print(lb.get_max_xor()) # Output should be the maximum xor sum achievable from these numbers. ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值