给一个序列,每次操作对这个序列中的所有数异或一个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字典树)的更多相关文章

  1. codeforces 842D Vitya and Strange Lesson

    题目大意: 定义mex数为数组中第一个没有出现的非负整数.有m个操作,每个操作有一个x,将数组中所有的元素都异或x,然后询问当前的mex Input First line contains two i ...

  2. Codeforces.842D.Vitya and Strange Lesson(Trie xor)

    题目链接 /* 异或只有两种情况,可以将序列放到01Tire树上做 在不异或的情况下在Tire上查找序列的mex很容易,从高位到低位 如果0位置上数没有满,则向0递归:否则向1 (0位置上的数都满了 ...

  3. 【cf842D】Vitya and Strange Lesson(01字典树)

    D. Vitya and Strange Lesson 题意 数列里有n个数,m次操作,每次给x,让n个数都异或上x.并输出数列的mex值. 题解 01字典树保存每个节点下面有几个数,然后当前总异或的 ...

  4. Codeforces Round #430 (Div. 2) Vitya and Strange Lesson

    D.Vitya and Strange Lesson(字典树) 题意: 给一个长度为\(n\)的非负整数序列,\(m\)次操作,每次先全局异或\(x\),再查询\(mex\) \(1<=n< ...

  5. Codeforces 948 数论推导 融雪前缀和二分check 01字典树带删除

    A. 全部空的放狗 B. 先O(NLOGNLOGN)处理出一个合数质因数中最大的质数是多少 因为p1 x1 x2的关系是 x2是p在x1之上的最小倍数 所以x1的范围是[x2-p+1,x2-1]要使最 ...

  6. Vitya and Strange Lesson CodeForces - 842D 字典树+交换节点

    题意: Today at the lesson Vitya learned a very interesting function - mex. Mex of a sequence of number ...

  7. 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 ...

  8. Codeforces Round #430 (Div. 2) D. Vitya and Strange Lesson

    因为抑或,一眼字典树 但是处理起来比较难 #include<iostream> #include<map> #include<iostream> #include& ...

  9. D. Vitya and Strange Lesson Codeforces Round #430 (Div. 2)

    http://codeforces.com/contest/842/problem/D 树 二进制(路径,每个节点代表一位) #include <cstdio> #include < ...

随机推荐

  1. selenium的显示等待和隐式等待的区别

    什么是显示等待和隐式等待?显示等待就是有条件的等待隐式等待就是无条件的等待 隐式等待 当使用了隐式等待执行测试的时候,如果 WebDriver没有在 DOM中找到元素,将继续等待,超出设定时间后则抛出 ...

  2. linux 内网时间同步配置

    在工作中,内网环境机器的时间会有所差异,在某些测试环境下需要一毫秒都不允许出现误差,但又不想同步外网时间,那我们可以选择一台机器作为时间服务器来供其他机器进行时间同步,例如每隔1分钟同步一次时间. 一 ...

  3. c# 常见文件操作

  4. QtCreator常用快捷键

    1)帮助文件:F1 (光标在函数名字或类名上,按 F1 即可跳转到对应帮助文档,查看其详细用法) 2).h 文件和对应.cpp 文件切换:F4 3)编译并运行:Ctrl + R 4)函数声明和定义(函 ...

  5. 优动漫PAINT提取线稿功能-亮度转化透明度

    优动漫PAINT在绘制漫画的过程中,难免会遇到图像质感的构建,那么我们如何将图像变得更加有质感,优动漫PAINT就相当人性化了.小白上手也是分分钟的事. 我们在进行绘画创作时,有时会忘了分层而将线稿直 ...

  6. Locust 教程

    写在 Locust 教程开始的前面 本文参考了: Locust 教程 : https://www.axihe.com/tools/locust/home.html : locust 的官方 Githu ...

  7. html布局-子div浮动后,父容器撑不开解决

    文章:子div撑不开父div的几种解决办法: 1,可以在所有子元素后增加<div style="clear:both;"></div> 2,给父容器增加样式 ...

  8. Integer Inquiry UVA-424(大整数)

    题意分析: 将字符串倒着存入int数组中,每次加完后再取余除去大于10的部分 关键:倒着存入,这样会明显缩短代码量. #include<iostream> #include<cstd ...

  9. Django解析器

    1.什么是解析器? 对请求的数据进行解析-请求体进行解析.解析器在你不拿请求体数据时,不会被调用. 安装与使用:(官方文档) https://www.django-rest-framework.org ...

  10. 4.Linq to Xml

    目录 1.Linq to Xml函数构造方法 2.创建包含文本节点的Xml文档 3.保存和加载Xml 4.处理Xml片段 5.从数据库中生成XML 1.Linq to Xml函数构造方法 Linq t ...