D2. Infinite Sequence (Hard Version)

题解:无限序列(困难版)


问题描述

给定一个正整数 $ n $ 和一个无限二进制序列的前 $ n $ 项,序列满足递归定义:

\[a_m = a_1 \oplus a_2 \oplus \cdots \oplus a_{\lfloor m/2 \rfloor} \quad \text{(对于 $ m > n $)} \]

其中 \(\oplus\) 表示按位异或。
现要求计算区间 \([l, r]\) 内所有序列元素的和,注意 $ l, r $ 可能非常大(最高达 \(10^{18}\)),而 $ n $ 的大小最多 \(2 \times 10^5\)


解题思路

1. 序列的自相似性与递归定义

  • 序列的递归定义使得 $ a_m \((\) m > n $)可以通过前 \(\lfloor m/2 \rfloor\) 个元素异或得到。
  • 这种定义导致序列具有“翻倍扩展”的自相似结构。例如,若 $ x $ 是某个下标,则其子序列 $ x \times 2^j \((\) j \geq 0 $)可以通过不断除以 2 缩放回较小范围进行计算。

2. 预处理部分

  • 输入前 $ n $ 项:读取并存储序列的前 $ n $ 项 $ a_1, a_2, \ldots, a_n $。
  • 构造前缀异或数组
    定义 \(\text{pre}[i] = a_1 \oplus a_2 \oplus \cdots \oplus a_i\),用于快速查询任意区间的异或结果。
  • 序列扩展
    若 $ n $ 为偶数,为了统一后续递归处理,将序列扩展一项,令

    \[a_{n+1} = \text{pre}[n/2] \]

    并更新前缀异或数组。

3. 递归计算单个位置 $ a_x $ 的值

设计一个函数 get(x),用于计算任意位置 $ x $ 的值:

  • 若 $ x \leq n $,直接返回 $ a_x $。
  • 若 $ x > n $,利用递归关系逐步将 $ x $ 归约到 \([1, n]\) 范围:
    • 将 $ x $ 不断除以 2,记录归约过程中每次的奇偶性变化。
    • 若新下标为偶数且大于 $ n $,则异或上 \(\text{pre}[n]\)
    • 最终通过预处理的 \(\text{pre}\) 数组得到结果。

4. 区间求和的分块处理

由于区间 \([l, r]\) 可能非常大,无法逐项递归计算。因此采用以下方法分块处理:

(1) 边界处理

  • 先将 \([l, r]\) 内属于前 $ n $ 项的部分直接加到答案中。
  • 对于 $ x > n $ 的部分,分别处理奇偶边界:
    • 若 $ r $ 为偶数,先单独处理 $ a_r $。
    • 若 $ l $ 为奇数,先单独处理 $ a_l $。
  • 剩下的区间保证起点为偶数、终点为奇数,方便后续缩放操作。

(2) 区间缩放

  • 将区间 \([l, r]\) 整体除以 2,即缩放为 \([\lceil l/2 \rceil, \lfloor r/2 \rfloor]\)
  • 计算下界时使用向上取整公式:

    \[L = \max(n+1, \lceil l/2 \rceil) = \max(n+1, (l+1)/2) \]

    确保缩放后下界不小于 $ n+1 $。
  • 上界直接向下取整:

    \[R = \lfloor r/2 \rfloor \]

(3) 区间内元素个数

  • 缩放后的区间 \([L, R]\) 中,元素个数为 $ R - L + 1 $,表示在当前层级下有多少个“缩放后的下标 $ a $”对应的原序号 $ a \times 2^j $ 落在 \([l, r]\) 内。

(4) 分层贡献的求和

  • 对于固定倍数因子 $ x = 2^j $,遍历缩放后的下标 $ a $:
    • 若 $ a \in [n/2+1, n] $,累加贡献:

      \[2 \times (t \oplus \text{pre}[a]) \]

      其中 $ t $ 是调整值,用于修正递归中的奇偶性变化。
    • 若 $ a > n $,利用区间内统一的贡献 $ t \oplus \text{pre}[n] $ 和元素个数 $ R - L + 1 $ 快速求和。

(5) 调整值 $ t $ 的含义

  • 由公式 $ t = \text{__builtin_ctzll}(x) & \text{pre}[n] $ 计算:
    • __builtin_ctzll(x) 返回 $ x $ 的二进制中最低有效位 \(1\) 出现的位置。
    • $ t $ 结合 \(\text{pre}[n]\) 的值,校正经过多次除以 2 后各层级的异或状态。
#include<bits/stdc++.h>
#define int long long
#define all(x) x.begin(),x.end()
#define rall(x) x.rbegin(),x.rend()
#define pb push_back
#define pii pair<int,int>
using namespace std;
const int mod=1e9+7;
int gcd(int a,int b){return b?gcd(b,a%b):a;};
int qpw(int a,int b){int ans=1;while(b){if(b&1)ans=ans*a%mod;a=a*a%mod,b>>=1;}return ans;}
int inv(int x){return qpw(x,mod-2);}
constexpr int N=1e5+10;
int dp[N][2];
vector<int>G[N];
void solve() {
    int n,l,r;cin>>n>>l>>r;
    vector<int>a(n+1),pre(n+1);
    for (int i=1;i<=n;i++)cin>>a[i],pre[i]=pre[i-1]^a[i];
    if (n%2==0) {
        a.pb(pre[n/2]),pre.pb(a[n+1]^pre[n]);
        n++;
    }
    auto get=[&](int x) {
        if (x<=n)return a[x];
        int res=0,i=x/2;
        while (i>n&&i%2==0) {
            res^=pre[n];
            i/=2;
        }
        res^=pre[min(i,n)];
        return res;
    };
    int ans=0;
    for (int i=l;i<=r&&i<=n;i++) {
        ans+=a[i];
    }
    if (r>n) {
        l=max(l,n+1);
        if (l<=r&&r%2==0) {//将其规整化为偶为l奇数为r的状态
            ans+=get(r);
            r--;
        }
        if (l<=r&&l%2==1) {
            ans+=get(l);
            l++;
        }
        if (l<=r) {
            l/=2,r/=2;//将其规整化除二 因为类似于 4 5 除二向下取整都为2 所有需要规整
            for (int x=1;x*(n/2+1)<=r;x*=2) {//每次填补半边 其扩展长度为原来的一倍
                int t=__builtin_ctzll(x)&pre[n];//实际含义为t=(pre[n])^log2(x)
                for (int a=n/2+1;a<=n&&a*x<=r;a++) {
                    if (a*x>=l) {
                        ans+=2*(t^pre[a]);//即为部分数除二之后一直存在偶数状态 且除二次数为log2(x)时 最后残留部分位于该区间内
                    }
                }
                //接下来处理奇数部分
                //对于这些能被缩放比例为x除完之后为奇数且可以直接转化为pre[n]的部分
                int R=r/x;//对其进行向下取整 确保区间内的每个缩放之后可以达成的个数
                int L=max((l-1+x)/x,n+1);//
                if (L <= R) {
                    ans += 2 * ((R - L + 1) / 2 + (L % 2) * (R % 2)) * (t ^ pre[n]);
                }
            }
        }
    }
    cout<<ans<<'\n';
}
signed main(){
    ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
    int _=1;
    cin>>_;
    while(_--)solve();
}
posted @ 2025-04-01 21:01  archer2333  阅读(66)  评论(0)    收藏  举报