CodeForeces 842d Vitya and Strange Lesson ——(带lazy标记的01字典树)
给一个序列,每次操作对这个序列中的所有数异或一个x,问每次操作完以后整个序列的mex值。
做法是去重后构建01字典树,异或x就是对root加一个x的lazy标志,每次pushDown时如果lazy的这一位是1,则交换左右儿子。找mex的话只要每次往左走,如果左子树是满的,则往右走,并且加上左边相应造成的贡献。具体见代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + ;
typedef long long ll; int n, m;
struct node
{
int sz, lazy;
node* ch[];
void init() {sz = ; lazy = ; ch[] = ch[] = NULL;}
}nodes[N<<], *root;
int tot; node* getnew()
{
node *p = &nodes[tot++];
p->init();
return p;
}
void insert(node *p, int dep, int x)
{
if(dep == -) return ;
int m = ((x>>dep) & );
if(p->ch[m] == NULL)
{
p->ch[m] = getnew();
}
insert(p->ch[m], dep-, x);
int szl = p->ch[] ? p->ch[]->sz : ;
int szr = p->ch[] ? p->ch[]->sz : ;
p->sz = szl + szr + ;
}
void down(node *p, int dep)
{
for(int i=;i<;i++)
{
if(p->ch[i]) p->ch[i]->lazy ^= p->lazy;
}
if((p->lazy >> dep) & ) swap(p->ch[], p->ch[]);
p->lazy = ;
}
void solve()
{
node *p = root;
int ans = ;
for(int i=;i>=;i--)
{
down(p, i);
if(p->ch[] && p->ch[]->sz == (<<i+) - )
{
ans |= (<<i);
p = p->ch[];
}
else
{
p = p->ch[];
}
if(!p) break;
}
printf("%d\n",ans);
} map<int, int> mp;
int main()
{
cin >> n >> m;
root = getnew();
for(int i=;i<=n;i++)
{
int x; scanf("%d",&x);
if(mp[x]) continue;
insert(root, , x);
mp[x] = ;
}
while(m--)
{
int x; scanf("%d",&x);
root->lazy ^= x;
solve();
}
return ;
}
还有一个类似的题目,之前青岛场的热身赛,每次需要求的是前k小的和。具体的做法类似,求和时如果左边的sz比k小则直接加上左边的sum并且向右边递归即可。这里需要注意,到当前节点是,如果要求其子树的sum,需要先对其子树进行pushDown,因为其sum已经在其子树要进行交换儿子的时候发生变化了,因此每次pushDown维护sum时需要用log次暴力地算出现在的sum,那么就需要对每个节点维护一个have数组,来记录各位上0和1的位数。具体见代码(含暴力对拍):
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + ;
typedef long long ll; int n, m;
struct node
{
int sz, lazy;
ll sum;
node* ch[];
int have[][];
void init() {sz = ; lazy = ; sum = ; ch[] = ch[] = NULL; memset(have, , sizeof have);}
}nodes[N<<], *root;
int tot; node* getnew()
{
node *p = &nodes[tot++];
p->init();
return p;
}
void insert(node *p, int dep, int x)
{
if(dep == -)
{
for(int i=;i>=;i--)
{
p->have[x >> i & ][i]++;
}
p->sz = ; p->sum = x; return ;
}
int m = ((x>>dep) & );
if(p->ch[m] == NULL)
{
p->ch[m] = getnew();
}
insert(p->ch[m], dep-, x);
int szl = p->ch[] ? p->ch[]->sz : ;
int szr = p->ch[] ? p->ch[]->sz : ;
p->sz = szl + szr;
ll suml = p->ch[] ? p->ch[]->sum : ;
ll sumr = p->ch[] ? p->ch[]->sum : ;
p->sum = suml + sumr;
//
//p->have[m][dep] ++;
for(int i=;i>=;i--)
{
p->have[][i] = p->have[][i] = ;
if(p->ch[]) p->have[][i] += p->ch[]->have[][i], p->have[][i] += p->ch[]->have[][i];
if(p->ch[]) p->have[][i] += p->ch[]->have[][i], p->have[][i] += p->ch[]->have[][i];
} /*if(x == 5 && dep == 2)
{
printf("%d ---- %d\n",p->have[0][2], p->have[1][2]);
}*/
}
void down(node *p, int dep)
{
if(dep == -)
{
p->sum ^= p->lazy;
p->lazy = ;
return ;
}
for(int i=;i<;i++)
{
if(p->ch[i]) p->ch[i]->lazy ^= p->lazy;
}
if((p->lazy >> dep) & )
{
/*if(p->ch[0])
{
p->ch[0]->sum += (1<<dep) * p->ch[0]->sz;
}
if(p->ch[1])
{
p->ch[1]->sum -= (1<<dep) * p->ch[1]->sz;
}*/
swap(p->ch[], p->ch[]);
}
//ll suml = p->ch[0] ? p->ch[0]->sum : 0;
//ll sumr = p->ch[1] ? p->ch[1]->sum : 0;
//p->sum = suml + sumr;
for(int i=;i>=;i--)
{
if(p->lazy >> i & )
{
p->sum += p->have[][i] * (<<i) - p->have[][i] * (<<i);
swap(p->have[][i], p->have[][i]);
}
}
p->lazy = ;
}
/*void solve()
{
node *p = root;
int ans = 0;
for(int i=20;i>=0;i--)
{
down(p, i);
if(p->ch[0] && p->ch[0]->sz == (1<<i+1) - 1)
{
ans |= (1<<i);
p = p->ch[1];
}
else
{
p = p->ch[0];
}
if(!p) break;
}
printf("%d\n",ans);
}*/
ll query(node *p, int k, int dep)
{
down(p, dep);
if(p->ch[] == NULL) return query(p->ch[], k, dep-);
//if(dep)
{
if(p->ch[]) down(p->ch[], dep-);
if(p->ch[]) down(p->ch[], dep-);
}
int szl = p->ch[]->sz;
//if(dep == 0 && k == 1) printf("%lld %lld ====== %lld\n",p->sum,p->ch[0]->sum, p->ch[1]->sum);
if(k == szl)
{
return p->ch[]->sum;
}
if(k < szl) return query(p->ch[], k, dep-);
else
{
// if(dep) down(p->ch[0], dep-1);
// if(dep == 2) printf("%lld ????? %d \n",p->ch[0]->sum,szl);
return p->ch[]->sum + query(p->ch[], k-szl, dep-);
}
} map<int, int> mp;
int a[N], b[N];
int main()
{
cin >> n >> m;
root = getnew();
for(int i=;i<=n;i++)
{
int x; scanf("%d",&x);
//if(mp[x]) continue;
insert(root, , x);
//mp[x] = 1;
a[i] = x;
}
while(m--)
{
int x, k; scanf("%d%d",&x,&k);
root->lazy ^= x;
printf("%lld -- ",query(root,k,));
for(int i=;i<=n;i++) {a[i]^=x; b[i] = a[i];}
for(int i=;i<=n;i++) printf("%d# ",a[i]); puts("");
sort(b+,b++n);
ll ans = ;
for(int i=;i<=k;i++) ans += b[i];
printf("%lld\n",ans);
}
return ;
}
/*
5 100
45 69 47 52 12 */
CodeForeces 842d Vitya and Strange Lesson ——(带lazy标记的01字典树)的更多相关文章
- codeforces 842D Vitya and Strange Lesson
题目大意: 定义mex数为数组中第一个没有出现的非负整数.有m个操作,每个操作有一个x,将数组中所有的元素都异或x,然后询问当前的mex Input First line contains two i ...
- Codeforces.842D.Vitya and Strange Lesson(Trie xor)
题目链接 /* 异或只有两种情况,可以将序列放到01Tire树上做 在不异或的情况下在Tire上查找序列的mex很容易,从高位到低位 如果0位置上数没有满,则向0递归:否则向1 (0位置上的数都满了 ...
- 【cf842D】Vitya and Strange Lesson(01字典树)
D. Vitya and Strange Lesson 题意 数列里有n个数,m次操作,每次给x,让n个数都异或上x.并输出数列的mex值. 题解 01字典树保存每个节点下面有几个数,然后当前总异或的 ...
- Codeforces Round #430 (Div. 2) Vitya and Strange Lesson
D.Vitya and Strange Lesson(字典树) 题意: 给一个长度为\(n\)的非负整数序列,\(m\)次操作,每次先全局异或\(x\),再查询\(mex\) \(1<=n< ...
- Codeforces 948 数论推导 融雪前缀和二分check 01字典树带删除
A. 全部空的放狗 B. 先O(NLOGNLOGN)处理出一个合数质因数中最大的质数是多少 因为p1 x1 x2的关系是 x2是p在x1之上的最小倍数 所以x1的范围是[x2-p+1,x2-1]要使最 ...
- Vitya and Strange Lesson CodeForces - 842D 字典树+交换节点
题意: Today at the lesson Vitya learned a very interesting function - mex. Mex of a sequence of number ...
- Codeforces Round #430 D. Vitya and Strange Lesson
Today at the lesson Vitya learned a very interesting function - mex. Mex of a sequence of numbers is ...
- Codeforces Round #430 (Div. 2) D. Vitya and Strange Lesson
因为抑或,一眼字典树 但是处理起来比较难 #include<iostream> #include<map> #include<iostream> #include& ...
- D. Vitya and Strange Lesson Codeforces Round #430 (Div. 2)
http://codeforces.com/contest/842/problem/D 树 二进制(路径,每个节点代表一位) #include <cstdio> #include < ...
随机推荐
- 【洛谷 P3966】 [TJOI2013]单词(AC自动机,差分)
把单词连起来,中间插入间隔符,同 #include <cstdio> #include <queue> #include <cstring> using names ...
- 【转载】C#中List集合使用Exists方法判断是否存在符合条件的元素对象
在C#的List集合操作中,有时候需要根据条件判断List集合中是否存在符合条件的元素对象,此时就可以使用List集合的扩展方法Exists方法来实现,Exists方法的签名为bool Exists( ...
- Oracle表数据转换为XML格式数据
转自:https://blog.csdn.net/smile_caijx/article/details/83352927 使用DBMS_XMLGEN可以解决问题 SELECT DBMS_XMLGEN ...
- EasyUI DataGrid 根据ID选中行(转载)
转载来源: https://blog.csdn.net/chq00788/article/details/51505519 function selectRows(){ //获取需要选中的记录ID ...
- Centos7安装autoconf
一.原因 安装此插件的原因:在初始化MySQL数据库时出现提示FATAL ERROR: please install the following Perl modules before executi ...
- Python如何打印文字对应的索引
用python编写一个简单的小程序:将文字对应的索引打印出来. test=input('>>>') print(test) l=len(test) print(l) r=range( ...
- 交互式计算引擎REOLAP篇
交互式计算引擎ROLAP篇 摘自:<大数据技术体系详解:原理.架构与实践> 一.Impala Impala最初由Cloudera公司开发的,其最初设计动机是充分结合传统数据库与大数据系 ...
- h5中history实例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- event.target事件
event.target <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...
- linux系统编程之信号(一)
今天起,开始新的知识的学习,对于上个系列进程的学习还差一个理论上的总结,这个会下次补回来,以便通过实践之后,再用理论将其巩固一下,好了,话不多说,开始进入这个主题的学习----信号,很重要,但不是太容 ...