NOJ——1669xor的难题(树状数组的异或求和)

  • [1669] xor的难题

  • 时间限制: 1000 ms 内存限制: 65535 K
  • 问题描述
  • 最近Alex学长有个问题被困扰了很久,就是有同学给他n个数,然后给你m个查询,然后每个查询给你l和r(左下标和右下标),然后问你每个查询l到r之间数字的xor值。(al ^ ... ^ ar)。

  • 输入
  • 输入t组数据,下一行输入n (1 <=n <=10^5)和m (1 <=m <= 10^4),第三行输入n个数字ai(0 <=ai <= 10^8),第四行输入m个询问l和r(1 <=l <=r <= n)。
  • 输出
  • 每个询问输出区间xor值的答案。
  • 样例输入
  • 1
    3 2
    0 1 2
    1 2
    3 3
  • 样例输出
  • 1
    2

    呃呃呃树状数组果然效果出众,从暴力800+ms变成了200+ms,学长深不可测,再感谢下阙神的指导,不然还不知道如何将树状数组扩展成异或形式求和。

    主要问题就是这个树状数组如何构建:

    首先建立一个一维(二维暂时没做到)的数组,作为树状数组的主体,然后运用add函数进行构建树状数组(每一个端点值附着在上面),然后用另一个getsum函数进行求和Sum[1~index]。当然一个区间和 Sum[L,R]=Sum[r]-Sum[l-1]。至此,一个树状数组普通求和就可以顺利进行了,那么异或求和例如an^an+1^......^am如何求和?通过阙神指导和之前的记忆,先把add和getsum中所有关于val(下标+号千万别动)的加号改为^号,然后最后答案的Sum[r]-Sum[l-1]中减号改为^号。

    代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdlib>
    #include<sstream>
    #include<cstring>
    #include<cstdio>
    #include<string>
    #include<deque>
    #include<cmath>
    #include<queue>
    #include<set>
    #include<map>
    using namespace std;
    const int n=100009;
    int tree[n];
    void add(int k,int val)  
    {  
        while(k<=n)  
        {  
            tree[k]^=val;//加号改^号  
            k+=k&-k;//这里是下标的操作,不能动 
        }  
    }  
    int getsum(int k)
    {  
        int sum=0;  
        while(k)  
        {  
            sum^=tree[k];//加号改^号
            k-=k&-k;/
        }  
        return sum;  
    }  
    int main(void)
    {
        int i,j,s,m,l,r,t,g;
        scanf("%d",&t);
        while(t--)
        {
        	memset(tree,0,sizeof(tree));
    		scanf("%d%d",&g,&m);
            for(i=1;i<=g;i++)
            {
                scanf("%d",&s);
                add(i,s);//枝点更新(附着)
            }
            for (i=0; i<m; i++)
            {
            	   scanf("%d%d",&l,&r);
            	   printf("%d\n",getsum(r)^getsum(l-1));//减号改^号
            }
        }
        return 0;
    }
### 关于级数求和问题的解题思路 对于南京邮电大学 NOJ 上的级数求和问题 (PROB1014),其核心在于找到满足条件 \( S(n) \geq K \) 的最小正整数 \( n \)[^1]。其中,\( S(n) = 1 + \frac{1}{2} + \frac{1}{3} + ... + \frac{1}{n} \) 是调和级数的部分和。 #### 数学背景 调和级数是一个发散级数,随着 \( n \to \infty \),部分和 \( S(n) \) 趋向无穷大。因此,给定任意实数 \( K \),总能找到一个最小的 \( n \) 满足 \( S(n) \geq K \)[^1]。 --- ### 示例代码实现 以下是基于 Python 实现的一个简单版本: ```python def find_min_n(k): n = 1 Sn = 0.0 # 初始化为浮点数以支持精确计算 while Sn <= k: Sn += 1 / n n += 1 return n - 1 # 返回最后一个使得Sn>k的n值 # 输入处理 try: k = float(input().strip()) result = find_min_n(k) print(result) except ValueError: print("输入错误,请确保输入的是一个有效的数值") ``` 上述代码通过循环逐步累加调和级数项直到超过目标值 \( K \),并返回对应的最小 \( n \) 值。 --- ### 性能优化考虑 尽管该算法能够解决问题,但在某些极端情况下可能会显得效率较低。例如当 \( K \) 非常大时,循环次数会显著增加。可以尝试利用近似公式来加速计算过程。已知调和级数可以通过欧拉-马歇罗尼常数 (\( \gamma \approx 0.5772156649 \)) 近似表达为: \[ H(n) \approx \ln(n) + \gamma + \frac{1}{2n} - \frac{1}{12n^2} + O\left(\frac{1}{n^4}\right). \] 这种方法适用于快速估算较大的 \( n \),但需要注意精度误差可能带来的影响。 --- ### 测试案例 为了验证程序正确性,可设计如下测试数据集: | **K** | **预期输出 N** | |-------|----------------| | 0 | 1 | | 1 | 2 | | 2 | 4 | | 3 | 11 | 运行以上代码即可得到相应结果并与理论值对比确认无误。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值