「知识学习&日常训练」莫队算法(一)(Codeforce Round #340 Div.2 E)
题意 (CodeForces 617E)
已知一个长度为\(n\)的整数数列\(a[1],a[2],…,a[n]\),给定查询参数\(l,r\),问\([l,r]\)内,有多少连续子段满足异或和等于\(k\)。
也就是说,对于所有的\(x,y (l\le x\le y\le r)\),能够满足\(a[x]\oplus a[x+1]\oplus ...\oplus a[y]=k\)的\((x,y)\)有多少组。
分析
对于这种离线区间的查询问题(不涉及对区间的更改),我们可以使用莫队算法解决。这类问题是什么类型?对于序列上的区间询问问题,如果从\([l, r]\)的答案能够\(O(1)\)扩展到\([l+1,r],[l,r−1],[l - 1, r],[l, r + 1]\)的答案,那么可以在\(O(n\sqrt n)\)的复杂度内求出所有询问的答案。
这题为什么可以?因为对于\(x\)至\(y\)的区间异或和,我们可以用前缀异或和的\(x-1\)与\(y\)相异或来解决。
接下来讲讲具体的实现:
(参考:https://blog.sengxian.com/algorithms/mo-s-algorithm)
实现:离线后排序,顺序处理每个询问,暴力从上一个区间的答案转移到下一个区间答案。
排序方法:设定块的长度为\(S\),按照\((\lfloor\frac l S\rfloor, r)\)二元组从小到大排序。
复杂度分析:设序列长度为\(n\),询问个数为\(m\),块内元素共有\(k\)个。注意到上面拓展左右区间的\(O(1)\)复杂度,我们分左端点右端点讨论:
a) 右端点:由于在块内递增,所以在整个块内迁移的复杂度下界是\(O(n)\);对于跨块而言最多从\(n\)迁回\(1\),所以也是\(O(n)\)(注意一下,这里讨论的维度是块,因为我们保证了块内的递增,所以一个块内的元素最多迁移\(n\)次);一共有\(\frac{n}{k}\)块,故总复杂度是\(O(\frac{n^2}{k})\);
b) 左端点:注意到我们分块时不维护递增,所以每次询问最多迁移\(k\)次;共有\(m\)次询问,故总复杂度是\(O(km)\)。
综上,莫队算法的总复杂度是\(O(\frac{n^2}{k}+km)\)。当这里\(k\)是变量,当\(m,n\)同一数量级时,取\(k = \sqrt n\)有最优复杂度\(O(n\sqrt n)\)。
这题的具体实现:我们记\(mp[x]\)为异或和为x的个数。转移区间的时候(不失一般性,考虑区间纯右移),每增加一个点\(r\),这个点对于答案的贡献是\(mp[x\oplus a[r]]\)(异或的性质),同时,它增加了\(mp[a[r]]\)的个数。每减少一个点同理。
代码
参考:https://blog.csdn.net/swust_lian/article/details/50615109
/*
* Filename: cfr340d2e.cpp
* Date: 2018-11-09
*/
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PB emplace_back
#define MP make_pair
#define fi first
#define se second
#define rep(i,a,b) for(repType i=(a); i<=(b); ++i)
#define per(i,a,b) for(repType i=(a); i>=(b); --i)
#define ZERO(x) memset(x, 0, sizeof(x))
#define MS(x,y) memset(x, y, sizeof(x))
#define ALL(x) (x).begin(), (x).end()
#define QUICKIO \
ios::sync_with_stdio(false); \
cin.tie(0); \
cout.tie(0);
#define DEBUG(...) fprintf(stderr, __VA_ARGS__), fflush(stderr)
using namespace std;
using pi=pair<int,int>;
using repType=int;
using ll=long long;
using ld=long double;
using ull=unsigned long long;
const int MAXN=100005;
const int BLOCK=400;
struct Node
{
ll l,r,id;
Node(ll _l=0, ll _r=0, ll _id=0):
l(_l), r(_r), id(_id) {}
bool operator < (const Node& rhs) const
{
if(l/BLOCK!=rhs.l/BLOCK) return l/BLOCK<rhs.l/BLOCK;
else return r<rhs.r;
}
};
vector<Node> vec;
ll s[MAXN];
ll ans[MAXN], mp[MAXN*200];
ll n,m,k;
int
main()
{
scanf("%lld%lld%lld", &n, &m, &k);
s[0]=0;
rep(i,1,n)
{
ll x; scanf("%lld", &x);
s[i]=s[i-1]^x;
}
rep(i,1,m)
{
ll l,r;
scanf("%lld%lld", &l, &r);
vec.PB(l-1,r,i); // why l-1: xor(a[x]~a[y])=k <-> s[x-1]^s[y]=k
}
sort(ALL(vec));
ZERO(mp);
ZERO(ans);
ll tmp=0;
int l=vec[0].l, r=vec[0].r;
rep(i,l,r)
{
tmp+=mp[s[i]^k];
mp[s[i]]++;
}
ans[vec[0].id]=tmp;
rep(i,1,m-1)
{
int L=vec[i].l,
R=vec[i].r;
while(l>L)
{
l--;
tmp+=mp[s[l]^k];
mp[s[l]]++; // mp: cnt of xor_sum = s[l]
}
while(l<L)
{
mp[s[l]]--;
tmp-=mp[s[l]^k];
l++;
}
while(r<R)
{
r++;
tmp+=mp[s[r]^k];
mp[s[r]]++;
}
while(r>R)
{
mp[s[r]]--;
tmp-=mp[s[r]^k];
r--;
}
ans[vec[i].id]=tmp;
}
rep(i,1,m) printf("%lld\n", ans[i]);
return 0;
}
「知识学习&日常训练」莫队算法(一)(Codeforce Round #340 Div.2 E)的更多相关文章
- 「日常训练」Kefa and Dishes(Codeforces Round #321 Div. 2 D)
题意与分析(CodeForces 580D) 一个人有\(n\)道菜,然后要点\(m\)道菜,每道菜有一个美味程度:然后给你了很多个关系,表示如果\(x\)刚好在\(y\)前面做的话,他的美味程度就会 ...
- 「日常训练」Kefa and Park(Codeforces Round #321 Div. 2 C)
题意与分析(CodeForces 580C) 给你一棵树,然后每个叶子节点会有一家餐馆:你讨厌猫(waht?怎么会有人讨厌猫),就不会走有连续超过m个节点有猫的路.然后问你最多去几家饭店. 这题我写的 ...
- 「日常训练」Kefa and Company(Codeforces Round #321 Div. 2 B)
题意与分析(CodeForces 580B) \(n\)个人,告诉你\(n\)个人的工资,每个人还有一个权值.现在从这n个人中选出m个人,使得他们的权值之和最大,但是对于选中的人而言,其他被选中的人的 ...
- 「日常训练」Case of Matryoshkas(Codeforces Round #310 Div. 2 C)
题意与分析(CodeForces 556C) 为了将所有\(n\)个娃娃编号递增地串在一起(原先是若干个串,每个串是递增的), 我们有两种操作: 拆出当前串中最大编号的娃娃(且一定是最右边的娃娃). ...
- 「日常训练」Brackets in Implications(Codeforces Round 306 Div.2 E)
题意与分析 稍微复杂一些的思维题.反正这场全是思维题,就一道暴力水题(B).题解直接去看官方的,很详尽. 代码 #include <bits/stdc++.h> #define MP ma ...
- 「日常训练」Divisibility by Eight(Codeforces Round 306 Div.2 C)
题意与分析 极简单的数论+思维题. 代码 #include <bits/stdc++.h> #define MP make_pair #define PB emplace_back #de ...
- 「日常训练」Paths and Trees(Codeforces Round 301 Div.2 E)
题意与分析 题意是这样的,定义一个从某点出发的所有最短路方案中,选择边权和最小的最短路方案,称为最短生成树. 现在求一棵最短生成树,输出总边权和与选取边的编号. 我们首先要明白这样一个结论:对一个图求 ...
- 「日常训练」Bad Luck Island(Codeforces Round 301 Div.2 D)
题意与分析(CodeForces 540D) 是一道概率dp题. 不过我没把它当dp做... 我就是凭着概率的直觉写的,还好这题不算难. 这题的重点在于考虑概率:他们喜相逢的概率是多少?考虑超几何分布 ...
- 「日常训练」Mike and Feet(Codeforces Round #305 Div. 2 D)
题意 (Codeforces 548D) 对一个有$n$个数的数列,我们要求其连续$x(1\le x\le n)$(对于每个$x$,这样的连续group有若干个)的最小数的最大值. 分析 这是一道用了 ...
随机推荐
- pcel安装的mongodb的两个问题的解决方案
最近工作需要,要使用mongodb,这个是使用 pecl 安装的,跟标准的 mongo 使用还是有区别的,这里不讲区别,只讲两个比较典型的问题该如何处理,具体的文档大家可以直接参考 php 的官方文档 ...
- Python 学习笔记(九)Python元组和字典(二)
什么是字典 字典是另一种可变容器模型,且可存储任意类型对象. 字典的每个键值 key=>value 对用冒号 : 分割,每个键值对之间用逗号 , 分割,整个字典包括在花括号 {} 中 键必须是唯 ...
- 时间比较方法DateTime.Compare
格式:DateTime.Compare(datetime1, datetime2) 参数为时间格式,为第一个参数比较第二个参数,返回小于0的值,等于0或大于0的值. 实例: string st1 = ...
- 工具 | Axure基础操作 No.2
不废话了,直接如之前一样上操作图才是正道. 1.设置文本类型为密码或者文件类型 可以在属性中也选择最大长度制定长度. 如果设置类型为文件,在浏览器中就会自动变成选择本地文件的按钮. 2.文本框提示文字 ...
- 键盘录入6个int类型的数据存入数组arr中,将arr数组中的内容反转...
一.有一道很有意思的数组操作相关编程题,闲来无事用JS解决了一下,问题描述如下: (1) 键盘录入6个int类型的数据存入数组arr中: (2) 将arr数组中的内容反转: (3) 将反转后的数组角标 ...
- GitHub Desktop 拉取 GitHub上 Tag 版本代码
一直在使用 GitHub Desktop 图形化 git 管理工具,统一项目框架版本时需要切换到ThinkPHP Tag 分支版本,步骤如下, 1,先在 GitHub 中找到需要的版本,点进去 2,点 ...
- Git单人本地仓库操作
本地仓库是个.git隐藏文件 以下为演示Git单人本地仓库操作 1.安装git sudo apt-get install git 密码:skylark 2.查看git安装结果 git 3.创建项目 在 ...
- shell基础知识---与监听服务器长连接端口状态
从未写过脚本我的最近接了俩脚本的需求,就在这分享一下我的我学到基础知识主要就四部分内容 一.变量 变量的定义 string='字符串' string="字符串" num=808st ...
- Virtualization state: Optimized (version 7.4 installed)
[Virtualization state: Optimized (version 7.4 installed)] [root@localhost ~]# cd /mnt/ [root@localho ...
- 初识Java——第一章 初识Java
1. 计算机程序: 为了让计算机执行某些操作或解决某个问题而编写的一系列有序指令的集合. 2. JAVA相关的技术: 1).安装和运行在本机上的桌面程序 2).通过浏览器访问的面向 ...