UVA8512线段树维护线性基

本文介绍了一种使用线性基和线段树解决特定数组查询问题的方法,通过预处理和优化,实现快速查找数组区间内元素异或和的最大值,再与给定值进行按位或运算得到最终答案。

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

​ 题目需要用到线性基,之前写过线性基的原理和模版,想看的同学点右边 -> 走你

题意:

Consider an array A with n elements . Each of its element is A[i] (1≤i≤n) . Then gives two integers Q, K, and Q queries follow . Each query , give you L, R, you can get Z by the following rules.

To get ZZ , at first you need to choose some elements from A[L] to A[R] ,we call them A[i1],A[i2]…A[it] , Then you can get number Z=K or (A[i1] xor A[i2] … xor A[it]) .

Please calculate the maximum Z for each query .

Input

Several test cases .

First line an integer T (1≤T≤10). Indicates the number of test cases.Then TT test cases follows . Each test case begins with three integer N, Q, K (1≤N≤10000, 1≤Q≤100000, 0≤K≤100000). The next line has N integers indicate A[1] to A[N] (0≤A[i]≤108). Then Q lines , each line two integer L, R (1≤L≤R≤N) .

Output

For each query , print the answer in a single line.

样例输入
1
5 3 0
1 2 3 4 5
1 3
2 4
3 5
样例输出
3
7
7

​ 翻译成汉语就是,给你一个T代表T组样例,每组样例有n, q, k,表示一个数组有n个数,有q次询问。下一行给出n个数分别是数组中每个数的值。接下来q行每行两个数l,r,问数组a[l] ~a[r]中选几个数的异或和|k的值最大

问最大值是多少?

思路:

​ 由于异或和|k值最大,这里有个小技巧。就是对数组中的每个数进行a[i] = a[i] & (~k),这样新的数组中最大的异或和|k就是最大值,因此我们只需要求新数组l~r的最大异或和

​ 由于n的数据范围是1e4,q的数据范围是1e5,如果每查询一次重新求一次线性基数组,肯定会超时。因此我们可以利用线段树来维护l~r之间的线性基数组。

​ 再加上各种剪枝,快速读写。用了不到600msA掉

代码:

#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <algorithm>
#define d int32_t
#define N 10005
#define lson rt<<1
#define rson rt<<1|1
#define mem(a) memset(a, 0, sizeof(a))
#define For(i, star, endd) for(d i = star; i <= endd; i++)
#define Forr(i, endd, star) for (d i = endd; i >= star; i--)
using namespace std;

//快速读
namespace IO {
    const d MX = 1e7;         //1e7 占用内存 11000kb
    char buf[MX];
    d c, sz;
    
    void begin() {            //该方法用于main方法开头位置,可一次性读完全部输入
        c = 0;
        sz = fread(buf, 1, MX, stdin);  //一次性全部读入
    }
    
    inline bool read(int &t) {        //用于从buf中读入每个数
        while (c < sz && buf[c] != '-' && (buf[c] < '0' || buf[c] > '9'))
            c++;
        if (c >= sz) return false;            //若读完整个缓冲块则退出
        bool flag = 0; if(buf[c] == '-') {
            flag = 1;
            c++;
        }
        for(t = 0; c < sz && '0' <= buf[c] && buf[c] <= '9'; c++)
            t = t * 10 + buf[c] - '0';
        if(flag) t = -t;
        return true;
    }
    
    inline void write(int t) {
        if (t > 9) write(t / 10);
        putchar(t % 10 + '0');
    }
}

//全局变量
d k;

//树节点(可存储范围内值的最高位maxn和线性基数组c)
struct node {
    d maxn;
    d c[35];
    void init() {
        mem(c);
    }
    
    void addx(d x) {
        Forr(i, 30, 0) {
            if(x >> i & 1) {
                maxn = i;
                c[i] = x;
                break;
            }
        }
    }
    
    d ansmax() {
        d t = 0;
        Forr(i, maxn, 0) {
            if ((t ^ c[i]) > t) {
                t ^= c[i];
            }
        }
        return t;
    }
    
    node merge(node A) {
        node t;
        t.init();
        Forr(i, maxn, 0) {
            t.c[i] = c[i];
        }
        Forr(i, A.maxn, 0) {
            if (A.c[i]) {
                d x = A.c[i];
                Forr(j, i, 0) {
                    if (x >> j & 1) {
                        if (!t.c[j]) {
                            t.c[j] = x;
                            break;
                        }
                        x ^= t.c[j];
                    }
                    if (x == 0) break;
                }
            }
        }
        t.maxn = max(maxn, A.maxn);
        return t;
    }
}shu[N << 2];

//建树
void Build (d rt, d l, d r) {
    if (l == r) {
        d t;
        IO::read(t);
        t = t & (~k);
        shu[rt].init();
        shu[rt].addx(t);
        return;
    }
    d mid = (l + r) >> 1;
    if (l <= mid) Build(lson, l, mid);
    if (mid < r) Build(rson, mid + 1, r);
    shu[rt] = shu[lson].merge(shu[rson]);
}

//查询
node request (d rt, d l, d r, d L, d R) {
    node t;
    t.init();
    if (l <= L && r >= R) {
        return shu[rt];
    }
    d mid = (L + R) >> 1;
    if (l <= mid && r > mid) return request(lson, l, r, L, mid).merge(request(rson, l, r, mid + 1, R));
    else if(l <= mid) return request(lson, l, r, L, mid);
    else if(r >= mid) return request(rson, l, r, mid + 1, R);
    else return t;
}

d main() {
    IO::begin();
    d T, n, q, l, r;
    IO::read(T);
    while (T--) {
        IO::read(n);
        IO::read(q);
        IO::read(k);
        Build(1, 1, n);
        For(i, 1, q) {
            IO::read(l);
            IO::read(r);
            node t = request(1, l, r, 1, n);
            d ans = (t.ansmax() | k);
            IO::write(ans);
            putchar('\n');
        }
    }
}

转载请注明出处!!!

如果有写的不对或者不全面的地方 可通过主页的联系方式进行指正,谢谢

#include <bits/stdc++.h> #define ll long long #define lb(x) x&(-x) using namespace std; const ll N=2e5; struct node{ ll l,r,suml,sumr,ans; }tr[4*N+5]; ll n,m,t; ll yy[N+5],pp[N+5],r[N+5],rr[N+5]; ll get(ll x,ll y){ ll s=0,ss=0; // cout<<x<<" "<<y<<" "; for(ll i=y-1;i;i-=lb(i)){ s+=rr[i]; } for(ll i=x-1;i;i-=lb(i)){ ss+=rr[i]; } // cout<<s-ss<<endl; return s-ss; } void updata(ll p,ll l,ll r){ ll mid=(l+r)>>1; tr[p].suml=min(tr[2*p].suml+get(mid,r),tr[2*p+1].suml); tr[p].sumr=min(tr[2*p+1].sumr+get(l,mid+1),tr[2*p].sumr); tr[p].ans=tr[2*p].suml+tr[2*p+1].sumr+get(mid,mid+1); tr[p].ans=min(tr[p].ans,min(tr[2*p].ans,tr[2*p+1].ans)); return; } void build(ll p,ll l,ll r){ if(l==r){ tr[p].l=l; tr[p].r=r; tr[p].suml=yy[l]; tr[p].sumr=pp[l]; tr[p].ans=yy[l]+pp[l]; return; } ll mid=(l+r)>>1; build(2*p,l,mid); build(2*p+1,mid+1,r); tr[p].l=tr[2*p].l; tr[p].r=tr[2*p+1].r; updata(p,tr[p].l,tr[p].r); return; } void change(ll p,ll x){ if(tr[p].l==tr[p].r){ tr[p].suml=yy[x]; tr[p].sumr=pp[x]; tr[p].ans=yy[x]+pp[x]; return; } ll mid=(tr[p].l+tr[p].r)>>1; if(x<=mid) change(2*p,x); else change(2*p+1,x); updata(p,tr[p].l,tr[p].r); return; } int main(){ scanf("%lld%lld%lld",&n,&m,&t); for(ll i=1;i<=n;i++) scanf("%lld",&yy[i]); for(ll i=1;i<=n;i++) scanf("%lld",&pp[i]); for(ll i=1;i<n;i++){ scanf("%lld",&r[i]); for(ll j=i;j<n;j+=lb(j)) rr[j]+=r[i]; } build(1,1,n); // for(ll i=1;i<=40;i++){ // cout<<tr[i].l<<" "<<tr[i].r<<" "; // cout<<tr[i].suml<<" "<<tr[i].sumr<<" "<<tr[i].ans<<endl; // } // return 0; printf("%lld\n",tr[1].ans); while(m--){ char op; ll x,y; cin>>op; scanf("%lld%lld",&x,&y); if(op=='r'){ for(ll i=x;i<n;i+=lb(i)){ rr[i]-=r[x]; rr[i]+=y; } r[x]=y; change(1,x),change(1,x+1); } else if(op=='p'){ pp[x]=y; change(1,x); } else{ yy[x]=y; change(1,x); } printf("%lld\n",tr[1].ans); } return 0; } ## **题目描述** 阿杰在游戏中管理着 $n$ 座编号为 $1$ 至 $n$ 的城市。每座城市有一个矿场和一个发电厂。他需要完成以下过程: 1. **采矿**:在任意城市 $i$ 的矿场开采矿物,花费 $y_i$(可为负,表示收益)。 2. **运输**:通过道路将矿物运送到任意城市 $j$ 的发电厂。相邻城市 $i$ 与 $i+1$ 间的道路费用为 $r_i$(双向且相同,可为负但不允许重复经过同一条道路)。 3. **发电**:在城市 $j$ 的发电厂消耗矿物发电,花费 $p_j$(可为负)。 **总花费**为三部分之和:$y_i + p_j + \text{运输路径费用}$。目标是选择起点 $i$ 和终点 $j$,使总花费最小(负花费表示净收益)。 游戏会动态更新费用:共 $m$ 个事件,每个事件将某个 $y_i$、$p_i$ 或 $r_i$ 修改为 $c$。需要实时输出每次更新后的最小总花费。 ## **输入格式** - **第一行**:$n, m, T$(城市数、事件数、测试点编号)。 - **第二行**:$n$ 个整数 $y_1, y_2, \dots, y_n$(采矿费用)。 - **第三行**:$n$ 个整数 $p_1, p_2, \dots, p_n$(发电费用)。 - **第四行**:$n-1$ 个整数 $r_1, r_2, \dots, r_{n-1}$(道路费用,$r_i$ 连接城市 $i$ 和 $i+1$)。 - **接下来 $m$ 行**:每行格式为 `t i c`: - $t \in \{ \texttt{y}, \texttt{p}, \texttt{r} \}$ 表示修改的费用类型。 - $i$ 表示被修改的城市编号($t=\texttt{r}$ 时 $i$ 表示道路编号,范围 $1 \le i \le n-1$)。 - $c$ 为新费用值(绝对值 $\le 10^{12}$)。 ## **输出格式** - **共 $m+1$ 行**: - 第 $1$ 行:初始状态的最小花费。 - 第 $2$ 至 $m+1$ 行:每次事件后的最小花费。 ## **样例** #### 输入样例 ```plaintext 10 10 0 12 12 17 28 42 55 60 73 73 91 89 89 79 77 54 38 34 24 24 0 2 4 2 1 4 3 7 10 10 p 2 92 p 3 87 r 4 3 r 1 9 r 2 4 r 5 3 y 10 72 y 8 67 r 2 0 y 4 91 ``` #### 输出样例 ```plaintext 53 53 53 55 55 55 54 54 54 50 50 ``` ## **数据范围** | 测试点 | $n$ | $m$ | 费用可为负 | $y_i=0$ | $p_i=0$ | |:------:|:-----:|:-------:|:------:|:----:|:-------:| | 1 | $500$ | $500$ | 否 | 否 | 否 | | 2 | $500$ | $500$ | 是 | 否 | 否 | | 3 | $3000$ | $3000$ | 否 | 否 | 否 | | 4 | $3000$ | $3000$ | 是 | 否 | 否 | | 5 | $2\times10^5$ | $2\times10^5$ | 是 | 是 | 是 | | 6–10 | $2\times10^5$ | $2\times10^5$ | 混合 | 混合 | 混合 | **特殊约束**: - 若“费用可为负”为**否**,则所有费用(含修改后)$\ge 0$。 - 若 $y_i=0$ 为**是**,则所有 $y_i=0$ 且无 `y` 类型事件;$p_i=0$ 同理。 --- ### **题目补充说明** 1. **道路特性**: - 道路双向通行且费用相同,但**不允许重复使用**(即使 $r_i<0$ 也不能多次经过同一条路获利)。 - 运输路径是简单路径(无环路)。 2. **发电过程**: - 采矿和发电必须在**不同或相同城市**完成(即 $i$ 和 $j$ 可相同,此时运输费用为 $0$)。 - 总花费计算式:$\text{min}_{1 \le i,j \le n} \left\{ y_i + p_j + \text{dis}(i,j) \right\}$,其中 $\text{dis}(i,j)$ 是 $i$ 到 $j$ 的路径费用和。 3. **事件限制**: - `r i c` 事件中 $i$ 为道路编号($1 \le i \le n-1$),修改连接 $i$ 和 $i+1$ 的道路费用。 代码答案错误,找出问题
最新发布
08-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值