【洛谷3822】[NOI2017] 整数(线段树压位)
题目:
分析:
直接按题意模拟,完了。
将每次加 / 减拆成不超过 \(32\) 个对单独一位的加 / 减。
考虑给一个二进制位(下称「当前位」)加 \(1\) 时,如果这一位本来就是 \(0\) ,那么直接变成 \(1\) 。否则要考虑进位:向左(以后默认从右向左为低位至高位,与书写顺序相同)找到第一个为 \(0\) 的位 \(p\) ,将其变成 \(1\) ,并把从 \(p\) 到当前位中间所有的 \(1\) 变成 \(0\) 。
减法是类似的。退位操作就是向左找到第一个 \(1\) ,将其变成 \(0\) ,并把中间所有 \(0\) 变成 \(1\) 。
以上找第一个 \(1\) 或者 \(0\) 和区间修改均可用线段树完成,只需要维护每个结点对应的区间是否全 \(0\) 或全 \(1\) 即可。
但是将一个询问拆成 \(32\) 次常数太大,\(3.2\times 10^7\) 次修改再带上线段树的 \(\log 3\times 10^7\) 根本过不去。考虑压位,线段树每个叶子表示连续多个(我的代码中使用的是 \(60\) 个)二进制位,找第一个 \(1\) / \(0\) 改为找第一个非 \(0\) (全 \(0\) ) / 非 \(2^{60}\) (全 \(1\) )的数。这样,每次修改只需要拆成最多对两个位置的加 / 减。复杂度 \(O(n\log m)\) 其中 \(m\) 是最大位数。
代码:
注意线段树上二分找第一个非全 \(0\) / 非全 \(1\) 的数的做法。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;
namespace zyt
{
template<typename T>
inline bool read(T &x)
{
char c;
bool f = false;
x = 0;
do
c = getchar();
while (c != EOF && c != '-' && !isdigit(c));
if (c == EOF)
return false;
if (c == '-')
f = true, c = getchar();
do
x = x * 10 + c - '0', c = getchar();
while (isdigit(c));
if (f)
x = -x;
return true;
}
template<typename T>
inline void write(T x)
{
static char buf[20];
char *pos = buf;
if (x < 0)
putchar('-'), x = -x;
do
*pos++ = x % 10 + '0';
while (x /= 10);
while (pos > buf)
putchar(*--pos);
}
typedef unsigned long long ull;
const int N = 1e6 + 10, DIGIT = 60;
const ull BASE = 1ULL << DIGIT;
namespace Segment_Tree
{
struct node
{
ull val;
bool all0, all1, tag0, tag1;
}tree[N << 2];
void cov0(const int rot)
{
tree[rot].val = 0;
tree[rot].all0 = tree[rot].tag0 = true;
tree[rot].all1 = tree[rot].tag1 = false;
}
void cov1(const int rot)
{
tree[rot].val = BASE - 1ULL;
tree[rot].all1 = tree[rot].tag1 = true;
tree[rot].all0 = tree[rot].tag0 = false;
}
void update(const int rot)
{
tree[rot].all0 = (tree[rot << 1].all0 && tree[rot << 1 | 1].all0);
tree[rot].all1 = (tree[rot << 1].all1 && tree[rot << 1 | 1].all1);
}
void pushdown(const int rot)
{
if (tree[rot].tag0)
{
cov0(rot << 1), cov0(rot << 1 | 1);
tree[rot].tag0 = false;
}
else if (tree[rot].tag1)
{
cov1(rot << 1), cov1(rot << 1 | 1);
tree[rot].tag1 = false;
}
}
void cover0(const int rot, const int lt, const int rt, const int ls, const int rs)
{
if (ls <= lt && rt <= rs)
{
cov0(rot);
return;
}
int mid = (lt + rt) >> 1;
pushdown(rot);
if (ls <= mid)
cover0(rot << 1, lt, mid, ls, rs);
if (rs > mid)
cover0(rot << 1 | 1, mid + 1, rt, ls, rs);
update(rot);
}
void cover1(const int rot, const int lt, const int rt, const int ls, const int rs)
{
if (ls <= lt && rt <= rs)
{
cov1(rot);
return;
}
int mid = (lt + rt) >> 1;
pushdown(rot);
if (ls <= mid)
cover1(rot << 1, lt, mid, ls, rs);
if (rs > mid)
cover1(rot << 1 | 1, mid + 1, rt, ls, rs);
update(rot);
}
void change(const int rot, const int lt, const int rt, const int pos, const ull x)
{
if (pos > rt)
return;
if (lt == rt)
{
tree[rot].val = x;
tree[rot].all0 = (x == 0);
tree[rot].all1 = (x == (BASE - 1ULL));
return;
}
int mid = (lt + rt) >> 1;
pushdown(rot);
if (pos <= mid)
change(rot << 1, lt, mid, pos, x);
else
change(rot << 1 | 1, mid + 1, rt, pos, x);
update(rot);
}
ull query(const int rot, const int lt, const int rt, const int pos)
{
if (lt == rt)
return tree[rot].val;
int mid = (lt + rt) >> 1;
pushdown(rot);
if (pos <= mid)
return query(rot << 1, lt, mid, pos);
else
return query(rot << 1 | 1, mid + 1, rt, pos);
}
int find0(const int rot, const int lt, const int rt, const int pos)
{
if (lt == rt)
return lt;
int mid = (lt + rt) >> 1;
pushdown(rot);
if (pos <= mid && !tree[rot << 1].all1)
{
int ans = find0(rot << 1, lt, mid, pos);
if (ans <= N)
return ans;
}
if (!tree[rot << 1 | 1].all1)
return find0(rot << 1 | 1, mid + 1, rt, pos);
else
return N + 1;
}
int find1(const int rot, const int lt, const int rt, const int pos)
{
if (lt == rt)
return lt;
int mid = (lt + rt) >> 1;
pushdown(rot);
if (pos <= mid && !tree[rot << 1].all0)
{
int ans = find1(rot << 1, lt, mid, pos);
if (ans <= N)
return ans;
}
if (!tree[rot << 1 | 1].all0)
return find1(rot << 1 | 1, mid + 1, rt, pos);
else
return N + 1;
}
void init()
{
cov0(1);
}
}
ull extract(const ull a, const int l, const int r)
{
return (a & ((1ULL << r) - 1ULL)) >> l;
}
bool check(const ull a, const int p)
{
return a & (1ULL << p);
}
void add(const ull a, const int p)
{
using namespace Segment_Tree;
ull now = query(1, 0, N, p);
if (now + a >= BASE)
{
int pos = find0(1, 0, N, p + 1);
ull tmp = query(1, 0, N, pos);
change(1, 0, N, pos, tmp + 1ULL);
if (pos > p + 1)
cover0(1, 0, N, p + 1, pos - 1);
}
change(1, 0, N, p, (now + a) % BASE);
}
void sub(const ull a, const int p)
{
using namespace Segment_Tree;
ull now = query(1, 0, N, p);
if (now < a)
{
int pos = find1(1, 0, N, p + 1);
ull tmp = query(1, 0, N, pos);
change(1, 0, N, pos, tmp - 1ULL);
if (pos > p + 1)
cover1(1, 0, N, p + 1, pos - 1);
}
change(1, 0, N, p, (now - a + BASE) % BASE);
}
int work()
{
using namespace Segment_Tree;
int n, t1, t2, t3;
read(n), read(t1), read(t2), read(t3);
init();
while (n--)
{
int opt;
read(opt);
if (opt == 1)
{
int a, b;
read(a), read(b);
if (a > 0) //ADD
{
add(extract(a, 0, DIGIT - b % DIGIT) << (b % DIGIT), b / DIGIT);
add(extract(a, DIGIT - b % DIGIT, DIGIT), b / DIGIT + 1);
}
else if (a < 0)
{
a = -a;
sub(extract(a, 0, DIGIT - b % DIGIT) << (b % DIGIT), b / DIGIT);
sub(extract(a, DIGIT - b % DIGIT, DIGIT), b / DIGIT + 1);
}
}
else
{
int k;
read(k);
write(check(query(1, 0, N, k / DIGIT), k % DIGIT) ? 1 : 0), putchar('\n');
}
}
return 0;
}
}
int main()
{
#ifdef BlueSpirit
freopen("3822.in", "r", stdin);
freopen("3822.out", "w", stdout);
#endif
return zyt::work();
}
【洛谷3822】[NOI2017] 整数(线段树压位)的更多相关文章
- [BZOJ4942][Noi2017]整数 线段树+压位
用线段树来模拟加减法过程,维护连续一段中是否全为0/1. 因为数字很大,我们60位压一位来处理. #include<iostream> #include<cstring> #i ...
- 洛谷3822 [NOI2017] 整数 【线段树】【位运算】
题目分析: 首先这题的询问和位(bit)有关,不难想到是用线段树维护位运算. 现在我们压32位再来看这道题. 对于一个加法操作,它的添加位置可以得到,剩下的就是做不超过32的位移.这样根据压位的理论. ...
- UOJ #314. 【NOI2017】整数 | 线段树 压位
题目链接 UOJ 134 题解 可爱的电音之王松松松出的题--好妙啊. 首先想一个朴素的做法! 把当前的整数的二进制当作01序列用线段树维护一下(序列的第i位就是整数中位权为\(2^k\)的那一位). ...
- 【BZOJ4942】[Noi2017]整数 线段树+DFS(卡过)
[BZOJ4942][Noi2017]整数 题目描述去uoj 题解:如果只有加法,那么直接暴力即可...(因为1的数量最多nlogn个) 先考虑加法,比较显然的做法就是将A二进制分解成log位,然后依 ...
- 2018.10.30 bzoj4942: [Noi2017]整数(线段树压位)
传送门 直接把修改的数拆成logloglog个二进制位一个一个修改是会TLETLETLE的. 因此我们把303030个二进制位压成一位储存在线段树里面. 然后维护区间中最靠左二进制位不为0/1的下标. ...
- 【BZOJ】1012: [JSOI2008]最大数maxnumber /【洛谷】1198(线段树)
Description 现在请求你维护一个数列,要求提供以下两种操作:1. 查询操作.语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值.限制:L不超过当前数列的长度.2. 插 ...
- 洛谷P1558 色板游戏 [线段树]
题目传送门 色板游戏 题目背景 阿宝上学了,今天老师拿来了一块很长的涂色板. 题目描述 色板长度为L,L是一个正整数,所以我们可以均匀地将它划分成L块1厘米长的小方格.并从左到右标记为1, 2, .. ...
- 洛谷题解P4314CPU监控--线段树
题目链接 https://www.luogu.org/problemnew/show/P4314 https://www.lydsy.com/JudgeOnline/problem.php?id=30 ...
- 洛谷P3372/poj3468(线段树lazy_tag)(询问区间和,支持区间修改)
洛谷P3372 //线段树 询问区间和,支持区间修改 #include <cstdio> using namespace std; struct treetype { int l,r; l ...
随机推荐
- swift-for循环遍历,遍历字典,循环生成数组
// Playground - noun: a place where people can play import UIKit //--------------------------------- ...
- leetcode——Implement strStr() 实现字符串匹配函数(AC)
Implement strStr(). Returns a pointer to the first occurrence of needle in haystack, or null if need ...
- [IT学习]转载python 项目 计算器
这个是从网上搜到的Python小项目之计算器(原文地址:http://www.2cto.com/kf/201402/279637.html).但该段代码估计是Python 2 写的. 如果你使用的程序 ...
- 2016/05/25 get和post的区别
get是从服务器上获取数据,post是向服务器传送数据. get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到.post是通过HTTP ...
- python set dict tuple and list
1 set 1.1 不变集合,frozenset 也就是说,集合中的元素不能删除,也不能增加. 1.2 两个集合之间的关系 isdisjoint()函数. 2 各个数据结构的不同显示 2.1 set ...
- YTU 2553: 谁是赢家
2553: 谁是赢家 时间限制: 1 Sec 内存限制: 128 MB 提交: 94 解决: 25 题目描述 某一天,hcbbt等一群弱菜去tamara巨巨家里一起玩了一个卡牌游戏.巨 巨家里有2 ...
- CollectionView缩放水平卡片布局
实现效果 实现思路 从Demo效果图中,可以看出来,主要是缩放系数的计算.对于不同距离的cell,其缩放系数要变化,以便整体协调显示. 所以,我们必须重写-layoutAttributesForEle ...
- 并不对劲的bzoj1861: [Zjoi2006]Book 书架
传送门-> 这题的正确做法是splay维护这摞书. 但是并不对劲的人选择了暴力(皮这一下很开心). #include<algorithm> #include<cmath> ...
- 并不对劲的bzoj1500: [NOI2005]维修数列
传送门-> 这题没什么好说的……小清新数据结构题……并不对劲的人太菜了,之前照着标程逐行比对才过了这道题,前几天刚刚把这题一遍写对…… 其实这题应该口胡很容易.操作1,2,3,4,5就是普通的s ...
- Windows下手动安装redis服务
1.Redis本身不支持windows,但是有另外的团队在维护着一个windows下的版本 2.下载地址: https://github.com/MSOpenTech/redis 2.8的版本在源 ...