【CodeForces 1100F】异或线性基 | 贪心 | 离线区间最大异或和 | E
补题ing… 真的不应该错过这场比赛的 qwq
1100F. Ivan and Burgers
time limit per test: 3 seconds memory limit per test: 256 megabytes input: standard input output: standard output
Tags:异或线性基 贪心
题目描述
给定 nnn 个数,有 qqq 次询问,每次询问 [l,r][l,r][l,r] 区间。
对于每次询问,可以选区间内任意多个数,求选数的最大异或和。
输入
111 组。第一行输入 nnn ,第二行输入 nnn 个数,第三行输入 qqq,
接下来 qqq 行每行两个数 lll 和 rrr,代表每次询问
范围:1≤n≤5×105,1≤q≤5×105,0≤ai≤1061 \le n \le 5 \times 10^5,1 \le q \le 5 \times 10^5,0 \le a_i \le 10^61≤n≤5×105,1≤q≤5×105,0≤ai≤106
输出
对于每次询问,输出一行一个数,即对应区间任意选数的最大异或和
输入样例 1
4
7 2 3 4
3
1 4
2 3
1 3
输出样例 1
7
3
7
输入样例 2
5
12 14 23 13 7
15
1 1
1 2
1 3
1 4
1 5
2 2
2 3
2 4
2 5
3 3
3 4
3 5
4 4
4 5
5 5
输出样例 2
12
14
27
27
31
14
25
26
30
23
26
29
13
13
7
分析
异或线性基
看到区间最大异或和,马上应该想到的是 异或线性基
这货和高代里面的“极大线性无关组”差不多,就是用这么些东西就可以表示整个空间中的一切其他值,同时内部又不能互相表示自己。异或线性基的意思就是,在一批数中选那么几个数,使得这几个数互相进行任意多次异或可以表示出没选中的任何数,并且又不能表示出自己。
要做到这一点,不妨按位考虑。比如有一组数,二进制是 0001,0010,0011,0100,0101,1000,1011,1100,11100001,0010,0011,0100,0101,1000,1011,1100,11100001,0010,0011,0100,0101,1000,1011,1100,1110。显然,我们随便拿一个最高位 111 是第四位的、再拿一个最高位 111 是第三位的、再拿一个最高位 111 是第二位的、再拿一个最高位 111 是第一位的,用这四个数就绝对可以表示出其他任何数了。
也就是我们拿的四个数应该长得像这个样子(星号表示为什么都可以)
1***
01**
001*
0001
比如拿 1000,0100,0010,00011000,0100,0010,00011000,0100,0010,0001 显然可以,并且拿 1011,0101,0011,00011011,0101,0011,00011011,0101,0011,0001 等等拿法也都可以。
为什么?因为通过异或:
- 最高位 111 是第一位的这个数可以控制任何数的第一位
- 最高位 111 是第一位的这个数和最高位 111 是第二位的这个数一起可以控制任何数的前两位
- 最高位 111 是第一位的这个数和最高位 111 是第二位的这个数和最高位 111 是第三位的这个数一起可以控制任何数的前三位
- …
因此我们只要把每一位都拿一个最高位在这个位置的就行了。
那要是这一组数某些位都是 000 怎么办?那就直接不管这一位了。
(比如 10010,10011,10001,00011,0000110010,10011,10001,00011,0000110010,10011,10001,00011,00001,那就分别拿最高位 111 是第五位的、是第二位的、是第一位的这么三个数即可。第三第四位大家都是 000,就不用管了)
那编程要怎么实现呢?首先可以确定的是,某组数的异或线性基的大小应该 ≤log2max{ai}\le \log_2 \max\{a_i\}≤log2max{ai}(最多只有这么多个位出现了 111)
然后我们开一个数组记录线性基的每一个数。很显然地,第 iii 个数应该就是表示最高位 111 在第 iii 位的那个数。
于是我们逐个遍历数组,如果发现当前数的最高位是 iii,并且线性基的那个位置还是空的话,那正好就收录这个数。
那如果那个位置已经有数了的话,我们是不是就直接抛弃这个数了呢?不行,因为这个数还是可能有利用价值的。我们拿这个数和线性基对应位置的数异或,得到的新数的最高位 111 一定在低于 iii 的位(设为 jjj 吧),然后如果线性基的 jjj 位置是空的话,显然我们可以把这个异或值给放进去(这个异或值虽然不是原来就存在的数,但是是当前遍历数和线性基里面已经有的数异或得到的,它当然也可以作为线性基的一部分。)
而如果我们抛弃这个数,就有可能没法得到线性基了。
比如 1111,1100,1110,00101111,1100,1110,00101111,1100,1110,0010,如果我们采取“抛弃”方法,最后线性基里面只有 111111111111 和 001000100010,显然没法表示完全。
而如果采取正确的方式,线性基里面就有 1111,0011(1111⊕1100),0001(1111⊕1110)1111,0011(1111\oplus 1100),0001(1111\oplus 1110)1111,0011(1111⊕1100),0001(1111⊕1110),显然谁都可以表示。
解题思路
再回到题目。我们要求的是区间最大异或值。
首先假设我们已经拿到了某个区间的异或线性基,我们怎么求出最大值呢?
一个 贪心 的想法是,我们从线性基最大的那个数(最高位 111 最高的那个数)开始往后遍历,如果异或上这个数可以让异或和变大我们就异或。最后累计的异或和就是最大异或和。
为什么这样是对的?首先,根据线性基的性质,我们一定不会遗漏某些异或值。因为一组线性基通过任意组合就已经能表示原来那组数的一切异或和了。
其次,为什么这样做得到的就是最大异或和呢?首先显然我们必须拿线性基最大的那个数。因为只有这个数才拥有那个最高最高位的 111,其他数的这一位都是 000。
然后接下来还是按位考虑,假设刚才选中了最高位 111 在第 iii 位的线性基,现在考虑最高位 111 在第 i−1i-1i−1 位的要不要也选上。显然,如果当前累计异或和的第 i−1i-1i−1 位是 000,那么不管第 i−2i-2i−2 及以后的位是什么,我们都必须拿线性基的最高位 111 在第 i−1i-1i−1 位的这个数;而如果当前累计异或和的第 i−1i-1i−1 位是 111,我们也一定不拿这个数。(因为以后再也没法影响这一位了)
但是这样设计代码就有点啰嗦了。而我们发现这个时候用 if(sum⊕a[i]>sum)if(sum\oplus a[i]\ >\ sum)if(sum⊕a[i] > sum) 语句可以起到等价的逻辑功能,所以这种贪心的求法就是正确的啦。
好,那么最后一个问题就是应该怎么应对多次询问了。借鉴 莫队 的思想,我们可以把询问全部收集起来然后按右端点排序,然后再按右端点升序动态插入维护线性基,然后回答每个询问就行啦。这样时间复杂度就不会爆炸了。
时间复杂度
- 排序询问 O(qlogq)O(q\log q)O(qlogq) 的。
- 循环 qqq 次动态维护线性基,每次插入和回答都是 O(log(max{ai}))O(\log(\max\{a_i\}))O(log(max{ai})) 的,总的 O(qlog(max{ai}))O(q\log(\max\{a_i\}))O(qlog(max{ai})) 的。
- 总时间复杂度: O(q(log(max{ai})+logq))O(\ q(\ \log(\max\{a_i\})+\log q\ )\ )O( q( log(max{ai})+logq ) ) 的。
AC代码
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
#include<string>
#include<algorithm>
#include<utility>
#include<vector>
#include<list>
#include<map>
#include<set>
#include<unordered_map>
#include<unordered_set>
#include<bitset>
#include<cctype>
#include<climits>#define GC getchar()
#define _SN(x) {char _c=GC,_v=1;for(x=0;_c<48||_c>57;_c=GC)if(_c==45)_v=-1;for(;_c>=48&&_c<=57;x=(x<<1)+(x<<3)+_c-48,_c=GC);if(_v==-1)x=-x;}
#define _SAN(a,n) {auto _i=0,_n=n;for(;_i<_n;++_i)_SN(a[_i])}
#define _SA(a,l,r) {auto _i=l,_r=r;for(;_i<_r;++_i)_SN(a[_i])}
#define _gS(_1, _2, _3, _sc, ...) _sc
#define sc(...) _gS(__VA_ARGS__,_SA,_SAN,_SN, ...)(__VA_ARGS__)
#define _G1(_1) int _1;sc(_1)
#define _G2(_1,_2) int _1,_2;sc(_1)sc(_2)
#define _G3(_1,_2,_3) int _1,_2,_3;sc(_1)sc(_2)sc(_3)
#define _gG(_1,_2,_3,_get, ...) _get
#define get(...) _gG(__VA_ARGS__,_G3,_G2,_G1, ...)(__VA_ARGS__)
#define _F0N(i,n) for(auto i=0;i<n;++i)
#define _FLR(i,l,r) for(auto i=l,_r=r;i<_r;++i)
#define _gF(_1, _2, _3, _F, ...) _F
#define F(...) _gF(__VA_ARGS__,_FLR,_F0N, ...)(__VA_ARGS__)
#define _FD0(i,n) for(auto i=0;i<=n;++i)
#define _FDL(i,l,r) for(auto i=l,_r=r;i<=_r;++i)
#define _gFD(_1, _2, _3, _FD, ...) _FD
#define FD(...) _gFD(__VA_ARGS__,_FDL,_FD0, ...)(__VA_ARGS__)#define OPER1(T,x1,b1) inline bool operator<(const T&o)const{return x1 b1 o.x1;}
#define OPER2(T,x1,b1,x2,b2) inline bool operator<(const T&o)const{return x1 b1 o.x1||x1==o.x1&&x2 b2 o.x2;}
#define OPER3(T,x1,b1,x2,b2,x3,b3) inline bool operator<(const T&o)const{return x1 b1 o.x1||x1==o.x1&&(x2 b2 o.x2||x2==o.x2&&x3 b3 o.x3);}#define LL long long
#define ULL unsigned long long
#define PC putchar
template<class T>
void PRT(const T _){if(_<0){PC(45),PRT(-_);return;}if(_>=10)PRT(_/10);PC(_%10+48);}
template<class T>
void UPRT(const T _){if(_>=10)UPRT(_/10);PC(_%10+48);}#define CON constexpr
#define T_CASE int CASE;sc(CASE)for(int __=1;__<=CASE;++__)
#define Tjj int T;sc(T)while(T--)
#define qjj int q;sc(q)while(q--)
#define cincout std::cin.tie(nullptr),std::cout.tie(nullptr),std::ios::sync_with_stdio(false);
#define eps 1e-8
#define PI 3.141592653589793
#define MAX_INT 2147483647
#define MIN_INT -2147483648
#define MAX_LL 9223372036854775807
#define MIN_LL -9223372036854775808
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3fLL
#define endl '\n'
#define priority_queue priority_queue
#define PQ std::priority_queue
#define PR std::pair
#define vector vector
#define VI std::vector<int>
#define MII std::map<int,int>
#define MLI std::map<LL,int>
#define MSI std::map<std::string,int>
#define PII std::pair<int,int>
#define PLI std::pair<LL,int>
#define PSI std::pair<std::string,int>
#define MPFD(k) auto it=mp.find(k)#define MIN(a, b) ((a)<(b)?(a):(b))
#define MIN3(a, b, c) (MIN(a, MIN(b, c)))
#define MAX(a, b) ((a)>(b)?(a):(b))
#define MAX3(a, b, c) (MAX(a, MAX(b, c)))
#define ABS(a) ((a)>0?(a):-(a))
#define FABS(a) ((a)>0?(a):-(a))
#define log2n(x) (log(x)/0.69314718055995)
#define MHD(p1, p2) ((p1.x>p2.x?p1.x-p2.x:p2.x-p1.x)+(p1.y>p2.y?p1.y-p2.y:p2.y-p1.y))#define PB emplace_back
#define EB emplace_back
#define BRK else break
#define ALL(X) (X).begin(),(X).end()
#define SORT(X) std::sort(ALL(X))
#define SORTD(X) std::sort(ALL(X),std::greater<decltype((X)[0])>())
#define SWAP(a, b) do{auto _t=a; a=b; b=_t;}while(0)
#define mem0(a) memset(a,0,sizeof(a))
#define memf1(a) memset(a,-1,sizeof(a))
#define meminf(a) memset(a,0x3f,sizeof(a))using bint = unsigned;CON bint ONE(1);
CON int MN(5e5+7), MB(20);struct Node
{int id;int l, r;OPER1(Node, r, <)
} q[MN];bint a[MN];int pos[MB];
bint base[MB];
bint ans[MN];bool insert(bint v, int p)
{for (int i=MB; i>=0; --i){if (v & (ONE << i)){if (base[i]){if (pos[i] < p)/* 更新,替换旧的、更靠前的基。因为:1.异或后产生的新的基的pos应该是两个异或加数的pos较小者2.扩大当前i位置的基的可用范围。就是说我们维护线性基的每一项的最大pos,这样在做get_max时才不会遗漏*/{SWAP(pos[i], p);SWAP(base[i], v);}v ^= base[i]; // 尝试找下一个基}else{base[i] = v;pos[i] = p;break;}}}return v;
}bint get_max(const int l)
{bint max = 0;for (int i=MB; i>=0; --i)if (pos[i]>=l && (max^base[i])>max)max ^= base[i];return max;
}int main()
{get(n)sc(a, n)get(k)F(i, k){sc(q[i].l)--q[i].l;sc(q[i].r)--q[i].r;q[i].id = i;}std::sort(q, q+k);int top = 0;F(i, k){while (top <= q[i].r){insert(a[top], top);++top;}ans[q[i].id] = get_max(q[i].l);}F(i, k)UPRT(ans[i]), PC(10);return 0;
}
【CodeForces 1100F】异或线性基 | 贪心 | 离线区间最大异或和 | E相关推荐
- Codeforces Round #532 (Div. 2) F. Ivan and Burgers(可持久化异或线性基+双指针)
题意 给n个数,q组询问,每次询问l到r的最大异或和 思路来源 某cf奆神代码 题解 本来应该是线性基上分治的 这里一发基数+贪心也能过 真是神仙代码啊 双指针的经典应用: 对于每个询问[l,r],r ...
- 【HDU3949 + BZOJ2115 + CF724G】【异或线性基例题】| 倍增 | 第k小异或和 | DFS处理环 |【CGWR】| N
三道关于异或线性基的有趣的题目 [1] HDU 3949. XOR Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/3276 ...
- 『线性空间 整数线性基和异或线性基』
线性空间 定义 线性空间是一个关于一下两个运算封闭的向量集合: \(1.\)向量加法\(a+b\),其中\(a,b\)为向量 \(2.\)标量乘法\(k*a\),其中\(a\)为向量,\(k\)为常数 ...
- P4570 [BJWC2011]元素(线性基+贪心)
题目链接:https://www.luogu.org/problem/P4570 题目大意: 给一个序列,在这个序列中选一些数,这些数的魔法值之和要最大,要求这些数中任意几个数的异或和不为0. 解题报 ...
- BZOJ2460: [BeiJing2011]元素(线性基+贪心)
BZOJ2460: [BeiJing2011]元素 Time Limit: 20 Sec Memory Limit: 128 MB Description 相传,在远古时期,位于西方大陆的 Magic ...
- 【BZOJ3105】新Nim游戏,线性基+贪心
Time:2016.09.08 Author:xiaoyimi 转载注明出处谢谢 思路: 思路题 看似简单,但是使用的知识还是很有意思(sang bing)的 首先就是关于Nim游戏 总之就是第一次取 ...
- 【BZOJ2460】元素,线性基+贪心
Time:2016.09.07 Author:xiaoyimi 转载注明出处谢谢 传送门 思路: 水题 有一个比较明显的性质 如果一个数x不能添加到已选集合中,那么有且只有一个已选集合中的子集A,使得 ...
- P4570-[BJWC2011]元素【线性基,贪心】
正题 题目链接:https://www.luogu.com.cn/problem/P4570 题目大意 给出nnn个物品有aia_iai和bib_ibi.要求选出一个bib_ibi和最大的子集满 ...
- [BZOJ]2460: [BeiJing2011]元素 线性基+贪心
Description 相传,在远古时期,位于西方大陆的 Magic Land 上,人们已经掌握了用魔 法矿石炼制法杖的技术.那时人们就认识到,一个法杖的法力取决于使用的矿石.一般地,矿石越多则法力越 ...
最新文章
- linux笔记:压缩解压命令gzip,gunzip,tar,zip,unzip,bzip2,bunzip2
- kernel: make tags 时的告警修改
- NO。58 新浪微博顶部新评论提示层效果——position:fixed
- “AlphaGo之父”获最新一届ACM计算奖
- adnroid开发环境的搭建
- 使用 GraalVM 将基本的 Java 项目打包成 EXE
- 中间件配置文件-redis
- LeetCode112. 路径总和(DFS)(递归)
- [转载] [转载] python中的Numpy库入门
- Swift3数组编辑
- IntelliJ IDEA使用教程(动图详解):Mac 系统下安装 IntelliJ IDEA
- PDF 文字识别网站
- 取石子游戏--尼姆博弈
- 超详细TMS-EEG数据处理教程(下)
- 网站安全渗透测试团队公司解决防护方案
- matlab 4维图读取,Matlab或mathmatica画4维图问题
- 确认OHS版本的方法
- [Java]Spring Ioc讲解,不怕你不懂
- java毕业设计大学生心理咨询管理系统mybatis+源码+调试部署+系统+数据库+lw
- 在抖音里怎么快速涨粉,抖音视频怎么发最容易上热门
热门文章
- 刺客信条 奥德赛的性能测试软件要求,《刺客信条:奥德赛》PC性能表现分析:非常流畅...
- python的drop duplicates_pandas.DataFrame.drop_duplicates 用法介绍
- 手把手教你利用工具进行视频搬运赚钱
- php5.3空间数据库,windows下 php5.3如何连接mssql
- JS有红,白,黑三球若干个,其中红,白球共25个,白黑共31个,红黑共28个,求三种球各多少个。
- Dlink路由器 CNVD-2018-01084 远程命令执行漏洞 复现分析
- 我有机器人合体成一个大力神_史上最菜大力神(七)——SS超载及大力神合体...
- Android 自定义彩色圆环,Android自定义View之酷炫数字圆环
- 10行python代码做出哪些酷炫的事情?
- 笔记本电源适配器为什么总坏_为什么某些交流适配器和电源会发出啸叫声?