• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
PCMSFV
博客园    首页    新随笔    联系   管理    订阅  订阅

NC312223 小橙的异或和

题意

题目本身就极其简洁

解题思路

这题需要在线维护操作一,不妨尝试手动用操作一,操作一段数,会发现除了a[l],其他都至少被除以2了(其实是,除以2, 3, 4, 5),又发现是下取整,也就是说,这个对于每个a[i]只要不一直在a[l]的位置上,在\(log(a[i])\)的次数内,一定会变成0,又因为这是异或和,是否异或0对结果是不会造成影响的。

所以在操作一(0除以任何不为0的数都为0)与操作二(0对异或和不造成影响)都是可以忽略0的。

因为a[i]最大是1e9,也就是只要同一个a[i]没有一直为a[l]的话,每一个a[i]变成0的次数的上限就是\(log(a[i])\),a[i] = 1e9时,这个数大概是30,也就是每一个数字被操作了30以下后(不是一直为a[l]),就变成0了,就可以被跳过了。

那如果操作一区间长度一直是1,怎么办?这样数组一直都不会变,那操作二每次遍历整个数组会不会超时?

结论是,对于操作二,每次操作如果遍历整个数组得到结果的话,必然超时(与下面的做法相比,做了大量重复操作,没有被修改的数字也要重新参与计算)。

为了解决这个情况,不妨维护一个ans,初始化为一开始整个数组的异或和。

在执行操作一的修改每个a[i]的过程中,可以这样:

ans ^= a[i]; // 消除修改前a[i]对ans的影响。
a[i] = a[i] / (i - l + 1); // 修改a[i]。
ans ^= a[i]; // 让新的a[i]对异或和造成影响。

这种情况,让区间少,修改就也少,避免了重复修改带来的时间累赘。

那如何跳过0呢,传统的遍历最多碰到0不执行,但是仍然会遍历到啊?

用并查集。

为什么是并查集呢?因为他把形如000...(一推0)X(末尾)看作了一个整体(X为正整数),而x就是这个并查集的代表元。

如果这个x的下标为i,那么find(i + 1) 就直接找到了,下一段的代表元,从而绕过了0。

如下图这样。

image-20260614104220822

这会有一种边界情况,比如全0了,代表元是谁?

创建虚拟节点n + 1,他的下标超过了所有区间的r,因此到这时,所有操作一会被直接跳过。

代码如下:

#include <bits/stdc++.h>

using namespace std;

#define int long long
constexpr int N = 2e5 + 2;   
int a[N], fa[N];

int find(int x) {
    if (x == fa[x]) return x;
    return fa[x] = find(fa[x]);
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    
    int n, q;
    cin >> n >> q;
    
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        ans ^= a[i];
        fa[i] = i;
    }
    fa[n + 1] = n + 1;
    
    while (q--) {
        int op;
        cin >> op;
        if (op == 1) {
            int l, r;
            cin >> l >> r;
            for (int i = find(l); i <= r; i = find(i + 1)) {
                ans ^= a[i];
                a[i] = a[i] / (i - l + 1);
                ans ^= a[i];
                if (!a[i]) fa[i] = find(i + 1);
            }
        } else {
            cout << ans << '\n';
        }
    }
}

难点

1.想到用并查集解决跳过元素。

总结

1.如果需要的结果是异或和,遇到修改一个a[i],然后得到修改后的异或和的题目,不用重新遍历求结果,可以利用异或两个一样的数等于没有异或。

2.遇到需要跳过(忽略)元素的题目,可以用并查集。让需要忽略的元素连接上代表元,跳过忽略的也就是查找后面的代表元,也就是跳过无效的不好想,就看看能不能找到下一个有效的(有点类似正难即反,而并查集的运用感觉有一点像分段(不要只专注元素个体,更要关注元素与元素之间的联系,比如他们同处同一个连续段、相邻等关系))。

对以后做题的启发

当一个区间操作会使元素值快速衰减到某个稳定状态(如零)时,可以用并查集维护‘下一个有效位置’,跳过已稳定的元素,从而将暴力遍历优化到近乎线性。

这题有一个不到 $ log $ 的”log“。

posted on 2026-06-14 10:59  PCMSFV  阅读(8)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3