首页 > 其他分享 >CF617E XOR and Favorite Number 题解

CF617E XOR and Favorite Number 题解

时间:2024-04-11 16:14:10浏览次数:11  
标签:cnt XOR text sum Favorite 异或 pf 题解 oplus

想了好久才明白zz

来源?:莫队题单


题目大意

给定一个长度为 \(n\) 的序列 \(a\),然后再给一个数字 \(k\),再给出 \(m\) 组询问,每组询问给出一个区间,求这个区间里面有多少个子区间的异或值为 \(k\)。

  • \(1 \le n,m \le 10^5\)
  • \(0 \le k,a_i \le 10^6\)
  • \(1 \le l_i,r_i \le n\)

题目分析

读完题后我是一脸懵的qwq

不会异或!!
不会查询!!

好,那就不要异或也不要查询!!(⁠・⁠o⁠・⁠)


简化 1

给定一个长度为 \(n\) 的序列 \(a\),求这个序列的和。

这也太简了

直接算 \(\sum\limits^{n}_{i=1} a_i\) 即可!


简化 2

给定一个长度为 \(n\) 的序列 \(a\),给出 \(m\) 组询问,每组询问给出一个区间,求这个区间的和。

参考:P8218 【深进1.例1】求区间和

蓝题变橙题

首先当然可以直接模拟,每次询问计算 \(\sum\limits_{i=l}^{r} a_i\)。可是时间复杂度 \(O(nm)\),不理想。

那么优化就是...

前缀和!

首先设 \(a_0=0\),因为无修改所以可以开一个数组 \(\text{pf}\) 记录 \(a\) 的前缀和。设

\[\text{pf}_x=\sum^{x}_{i=0} a_i \]

我们有

\[\begin{align*} \sum^{r}_{i=l}a_i&=\sum^{r}_{i=0}a_i-\sum^{l-1}_{i=0}a_i\\ &= \text{pf}_r-\text{pf}_{l-1} \end{align*} \]

时间复杂度:\(O(n)\) 预处理,\(O(1)\) 查询


简化 3

给定一个长度为 \(n\) 的序列 \(a\),给出 \(m\) 组询问,每组询问给出一个区间,求这个区间的异或值。

参考:Leetcode 1310. 子数组异或查询

首先定义 \(\oplus\) 为异或符号,\(\bigoplus\limits_{i=l}^{r}a_i=a_l \oplus a_{l+1} \oplus \dots \oplus a_{r-1} \oplus a_r\)。

发现这题与上一题非常相似,考虑是否可以利用同样的思想。

考虑 \(a_0\) 的值。当我们在求和时,我们设 \(a_0=0\)。 为什么?

是因为我们知道对于任意 \(x\),\(x+0=x\) 成立。在这里我们称 \(0\) 为加法的单位元。

同理对于任意 \(x\),\(x \times 1=x\) 成立。所以 \(1\) 为乘法的单位元。

那么异或呢?对于任意 \(x\),\(x \oplus 0=x\) 成立。所以 \(0\) 为异或的单位元。

所以我们同样设 \(a_0=0\)。

继续开一个数组 \(\text{pf}\) 记录 \(a\) 的前缀异或。设

\[\text{pf}_x=\bigoplus\limits_{i=0}^{x} a_i \]

要如何得到区间异或呢?同样先从和观察。

\[\begin{align*} \sum_{i=0}^{l-1}a_i + \sum_{i=l}^{r}a_i &= \sum_{i=0}^{r}a_i\\ \sum_{i=l}^{r}a_i &= \sum_{i=0}^{r}a_i - \sum_{i=0}^{l-1}a_i\\ &= \text{pf}_r - \text{pf}_{l-1} \end{align*} \]

注意到第二步我们将 \(+\) 号变成了 \(-\) 号,为什么?

因为减法是加法的逆运算!对于任意 \(u,v,w\) 满足 \(u+v=w\),\(u=w-v\) 成立。

那么异或呢?观察发现异或的逆运算是异或!

也就是对于任意 \(u,v,w\) 满足 \(u \oplus v = w\),\(u = w \oplus v\) 成立。

因为异或满足交换律,所以 \(u = v \oplus w\) 也成立。

回到前缀异或。现在我们有

\[\begin{align*} \bigoplus_{i=0}^{l-1}a_i \oplus \bigoplus_{i=l}^{r}a_i &= \bigoplus_{i=0}^{r}a_i\\ \bigoplus_{i=l}^{r}a_i &= \bigoplus_{i=0}^{r}a_i \oplus \bigoplus_{i=0}^{l-1}a_i\\ &= \text{pf}_r \oplus \text{pf}_{l-1} \end{align*} \]

时间复杂度:\(O(n)\) 预处理,\(O(1)\) 查询


简化 4

给定一个长度为 \(n\) 的序列 \(a\),然后再给一个数字 \(k\),求这个序列里面有多少个子区间的和为 \(k\)。

参考:Leetcode 560. 和为 K 的子数组

这里开始就有一点难度了 主要是我菜

发现可以转化题目为

给定一个长度为 \(n\) 的序列 \(a\),然后再给一个数字 \(k\),求有多少个 \(l,r\) 满足 \(\sum\limits_{i=l}^{r} a_i=k\)。

直接遍历可得 \(O(n^3)\) 解法

int ans=0;
for(int len=1;len<=n;len++){
    for(int l=0;l<=n-len;l++){
        int r=l+len-1,sum=0;
        for(int i=l;i<=r;i++){
            sum+=a[i];
        }
        if(sum==k) ans++;
    }
}
时间复杂度证明 $$ \begin{align*} \sum_{L=1}^{n}\sum_{l=0}^{n-L}\sum_{i=l}^{l+L-1} 1 &= \sum_{L=1}^{n}\sum_{l=0}^{n-L} L\\ &= \sum_{L=1}^{n} (L(n-L+1))\\ &= \sum_{L=1}^{n} (nL-L^2+L)\\ &= \sum_{L=1}^{n} nL - \sum_{L=1}^{n} L^2 + \sum_{L=1}^{n} L\\ &= n \left(\sum_{L=1}^{n} L\right) + \sum_{L=1}^{n} L - \sum_{L=1}^{n} L^2\\ &= (n+1)\left(\sum_{L=1}^{n} L\right) - \sum_{L=1}^{n} L^2\\ &= (n+1)\left(\dfrac{n(n+1)}{2}\right) - \dfrac{n(n+1)(2n+1)}{6}\\ &= \dfrac{1}{2}\left(n^3+2n^2+n\right) - \dfrac{1}{6}\left(2n^3+3n^2+n\right)\\ &= \dfrac{1}{6}\left(3n^3+6n^2+3n\right) - \dfrac{1}{6}\left(2n^3+3n^2+n\right)\\ &= \dfrac{1}{6}\left(n^3+9n^2+4n\right) \end{align*} $$ 可得 $$O\left(\dfrac{1}{6}\left(n^3+9n^2+4n\right)\right) = O(n^3)$$

利用前缀和可以优化成 \(O(n^2)\)

能继续优化吗?

观察我们可以利用简化 2 的思想得到

给定一个长度为 \(n\) 的序列 \(a\),然后再给一个数字 \(k\),求有多少个 \(l,r\) 满足 \(\text{pf}_r-\text{pf}_{l-1}=k\)。

\(l \le r\) 必定成立。

遍历 \(r=[1,n]\),发现区间 \([l,r]\) 要满足 \(\text{pf}_r-\text{pf}_{l-1}=k\),必定有 \(\text{pf}_{l-1}=\text{pf}_r-k\)。

定义 \(cnt_x\) 为 \(x\) 出现的次数,初始化 \(cnt_x=0\)。因为 \(l \le r\) 成立,所以对于任意 \(r\),\(l-1\) 的取值为 \([0,r-1]\)。因此仅需在查询 \(cnt_{\text{pf}_r-k}\) 后将 \(cnt_{\text{pf}_r}\) 的值加上 \(1\) 即可。

可得答案为

\[\sum_{r=0}^{n}cnt_{\text{pf}_r-k} \]

需要注意的是 \(a_i\) 的大小。当

  • \(1 \le n \le 10^5\)
  • \(0 \le a_i \le 10^6\)

\(\text{pf}_i\) 的最大值为 \(10^5 \times 10^6 = 10^{11}\),会 MLE。可以用 STL map 来维护,单次查询 \(O(\log n)\)。

为什么不用 unordered_map 有机会被卡成单次 $O(n)$,总 $O(n^2)$

详情见 Blowing up unordered_map, and how to stop getting hacked on it

总时间复杂度:\(O(n \log n)\)


简化 5

给定一个长度为 \(n\) 的序列 \(a\),然后再给一个数字 \(k\),求这个序列里面有多少个子区间的异或值为 \(k\)。

合并简化 3 和简化 4 的结论,答案就是

\[\sum_{r=0}^{n}cnt_{\text{pf}_{r} \oplus k} \]

如果明白可以跳过这个 利用简化 3 转化题目为

给定一个长度为 \(n\) 的序列 \(a\),然后再给一个数字 \(k\),求有多少个 \(l,r\) 满足 \(\text{pf}_r \oplus \text{pf}_{l-1}=k\)。

\(l \le r\) 必定成立。

遍历 \(r=[1,n]\),发现区间 \([l,r]\) 要满足 \(\text{pf}_r \oplus \text{pf}_{l-1}=k\),必定有 \(\text{pf}_{l-1}=\text{pf}_r \oplus k\)。

定义 \(cnt_x\) 为 \(x\) 出现的次数,初始化 \(cnt_x=0\)。因为 \(l \le r\) 成立,所以对于任意 \(r\),\(l-1\) 的取值为 \([0,r-1]\)。因此仅需在查询 \(cnt_{\text{pf}_r \oplus k}\) 后将 \(cnt_{\text{pf}_r}\) 的值加上 \(1\) 即可。

可得答案为

\[\sum_{r=0}^{n}cnt_{\text{pf}_r \oplus k} \]


题目解法

经过多轮的简化已经看得出原题了!贴回原题:

给定一个长度为 \(n\) 的序列 \(a\),然后再给一个数字 \(k\),再给出 \(m\) 组询问,每组询问给出一个区间,求这个区间里面有多少个子区间的异或值为 \(k\)。

最后要处理的是回答 \(m\) 组询问。终于要请出今天的主角:莫队算法!

不过经费不足所以腰斩了有机会再更

莫队算法可以参考这位dalao的博客:莫队算法——从入门到黑题

利用简化 5 的结论维护数组 \(cnt\)。在移动 \(l,r\) 指针时更新 \(cnt\)。对于区间 \([l,r]\),答案为

\[\sum_{i=l-1}^{r}cnt_{\text{pf}_i \oplus k} \]

注意:因为 \(a_i \le 10^6\),如果用数组维护 \(cnt\),需要将容量设为 \(2^{\lceil\log_2 10^6\rceil}=2^{20}=\) 1<<20


代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

ll n,m,k;
ll a[100020],pf[100020];
ll sq,bnum;
ll blg[100020],cnt[1<<20];

struct Mo{
    ll l,r,id;
    bool operator <(const Mo &rhs) const{
        return blg[l]==blg[rhs.l] ? r<rhs.r : l<rhs.l;
    }
};

Mo query[100020];
ll ans=0;

void add(ll x){
    ans+=cnt[pf[x]^k];
    cnt[pf[x]]++;
}

void del(ll x){
    cnt[pf[x]]--;
    ans-=cnt[pf[x]^k];
}

int main() {
    cin>>n>>m>>k;
    a[0]=0;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    pf[0]=a[0];
    for(int i=1;i<=n;i++){
        pf[i]=pf[i-1]^a[i];
    }
    sq=sqrt(n);bnum=ceil((double)n/sq);
    for(int i=0;i<=bnum;i++){
        for(int j=0;j<sq;j++){
            if(i*sq+j+1>n)break;
            blg[i*sq+j+1]=i;
        }
    }

    for(int i=0;i<m;i++){
        ll x,y;cin>>x>>y;
        query[i].l=--x;
        query[i].r=y;
        query[i].id=i;
    }
    sort(query,query+m);
    memset(cnt,0,sizeof(cnt));
    vector<ll> output(m);
    ll L=0,R=-1;
    for(int i=0;i<m;i++){
        ll l=query[i].l,r=query[i].r,id=query[i].id;
        while(L<l) del(L++);
        while(L>l) add(--L);
        while(R<r) add(++R);
        while(R>r) del(R--);
        output[id]=ans;
    }

    for(ll x:output)cout<<x<<'\n';
}


后记

是道好题,题解写了好久qwq

如果有任何疑问或错误漏缺欢迎在评论告诉我!

~~~ 完结撒花 ✿✿ヽ(°▽°)ノ✿ ~~~

标签:cnt,XOR,text,sum,Favorite,异或,pf,题解,oplus
From: https://www.cnblogs.com/lumid/p/18119967

相关文章

  • 排序规则冲突问题解决
    --英文操作系统数据库恢复到中文版本操作系统的时候容易出现一下问题--无法解决equalto运算中"SQL_Latin1_General_CP1_CI_AS"和"Chinese_PRC_CI_AS"之间的排序规则冲突。--简单解决办法如下:指定排序规则COLLATESQL_Latin1_General_CP1_CI_AS或者Chinese_PRC_CI_AS......
  • 【题解】CF311E Biologist题解
    CF311EBiologist题解非常好的一道最小割。思路首先看到每一个位置又会有\(01\)两种情况,然后要满足一些要求,求最大收益,考虑类似于P4313文理分科和P1361小M的作物这种集合划分的建图方法,也就是要用最小割求解。由于我们要求的是最大收益,所以我们要先明确要最小化什么,......
  • 【题解】CF1187G Gang Up
    【题解】CF1187GGangUp题意给定一个图,有\(k\)个人要走到\(1\)号节点,问最小花费。解法一眼丁真,鉴定为费用流。考虑到这道题花费会与时间有关,所以分层图,启动!。按时刻分层,现在分析每个人在第\(k\)时刻的动向:1.呆着不动。2.走到下一个节点。对于动向\(1\),从时......
  • 洛谷 P6692 题解
    洛谷P6692出生点题意简述\(n\)行\(m\)列构成\(nm\)个格点,在其中指定\(k\)个障碍点。每行、每列之间的距离为\(1\),每次任意选取两个非障碍点,计算这两个点的曼哈顿距离,求所有选法的距离之和。分析由容斥原理,答案为「任意两点之间的距离之和」\(-\)「每个障碍点到其他......
  • CF358B Dima and Text Messages 题解
    大家好,我不喜欢string,所以我选择用char来写。题目传送门,但不是洛谷。吐槽一下,这个翻译翻译的并不好,翻译中并没有说明“爱心”是指<3,还是得去自己翻。正文将读入的单词连在一起,并穿插爱心,注意这里首尾都是爱心,需要手动补充。最后将得到的序列与输入的字符串进行比对即可。......
  • 2024年3月电子学会青少年软件编程 中小学生Python编程等级考试一级真题解析(判断题)
    2024年3月Python编程等级考试一级真题解析判断题(共10题,每题2分,共20分)26、turtle画布的坐标系原点是在画布的左上角答案:错考点分析:考查turtle相关知识,turtle画布坐标系是在画布的中点,答案错误27、Python变量名区分大小写,book和BOOK不是同一个变量答案:对考点分析:考查......
  • CF1250A Berstagram 题解
    题面。题意描述的很清楚,这里就不多赘述。思路看题,对于每个\(a_i\),若\(b_j=a_i\),则将\(b_j\)与\(b_{j-1}\)的值调换(若\(j=1\),则序列不变)。一开始想的是最直接的暴力,复杂度为\(O(nm)\),虽然开了三秒的时限,但\(4\times10^{10}\)的数据明显不是三秒钟就能解决的,含恨倒在第......
  • P8791 [蓝桥杯 2022 国 AC] 内存空间 题解
    题面一道比较简单模拟题,但是要分类讨论一.读完题你应该知道的1.输入一共有T+1行,输入含有空格。(处理1)2.对于每一行变量定义的语句,只会出现一种变量类型,type只可能是int或long,只有一个分号。(处理1,处理2)3.统计内存过程中用B做单位,保证一定有输出,但在输出时要换......
  • CF1681C Double Sort 题解
    一道普及-我写了两个半小时题面。需要注意的是,每次交换需要将a和b两个数组同时交换,因此便可以想到唯一可行情况:a,b序列数字间的大小关系必须一致。举个例子2462131317970612在上面的例子中,两个序列中任意\(i\)和\(j\)满足\(a_i\lea_j\)时\(b_i......
  • CF958F1 Lightsabers (easy) 题解
    题面。不好意思,当我看到\(1\len\le100\),\(1\lem\len\)的那一刻,我承认我心动了。题目没翻译,用翻译软件翻译了才看懂。思路依据题意直接模拟即可。这里我用了三层循环来模拟。第一层为\(len\),表示长度;第二层为\(from\),表示出发点,此时需要遍历的区间的终点\(to=from......