NOIP2017整数 【线段树】

题目

题目背景

在人类智慧的山巅,有着一台字长为10485761048576 位(此数字与解题无关)的超级计算机,著名理论计算机科

学家P博士正用它进行各种研究。不幸的是,这天台风切断了电力系统,超级计算机

无法工作,而 P 博士明天就要交实验结果了,只好求助于学过OI的你. . . . . .

题目描述

P 博士将他的计算任务抽象为对一个整数的操作。

具体来说,有一个整数xx ,一开始为00 。

接下来有nn 个操作,每个操作都是以下两种类型中的一种:

1 a b:将xx 加上整数a\cdot 2^ba?2

b

,其中aa 为一个整数,bb 为一个非负整数

2 k :询问xx 在用二进制表示时,位权为2^k2

k

的位的值(即这一位上的11 代表 2^k2

k

保证在任何时候,x\geqslant 0x?0 。

输入格式

输入的第一行包含四个正整数n,t_1,t_2,t_3n,t

1

? ,t

2

? ,t

3

? ,nn 的含义见题目描述,t_1t

1

? ,t_2t

2

? ,t_3t

3

? 的具体含义见子任务。

接下来nn 行,每行给出一个操作,具体格式和含义见题目描述。

同一行输入的相邻两个元素之间,用恰好一个空格隔开。

输出格式

对于每个询问操作,输出一行,表示该询问的答案(00 或11 )。对于加法操作,没有任何输出。

输入样例

10 3 1 2

1 100 0

1 2333 0

1 -233 0

2 5

2 7

2 15

1 5 15

2 15

1 -1 12

2 15

输出样例

0

1

0

1

0

提示

在所有测试点中,1\leqslant t_1 \leqslant 3, 1 \leqslant t_2 \leqslant 4, 1 \leqslant t_3 \leqslant 21?t

1

? ?3,1?t

2

? ?4,1?t

3

? ?2 。不同的 t_1, t_2, t_3t

1

? ,t

2

? ,t

3

? 对应的特殊限制如下:

对于 t_1 = 1t

1

? =1 的测试点,满足 a = 1a=1

对于 t_1 = 2t

1

? =2 的测试点,满足 |a| = 1∣a∣=1

对于 t_1 = 3t

1

? =3 的测试点,满足 |a| \leqslant 10^9∣a∣?10

9

对于 t_2 = 1t

2

? =1 的测试点,满足 0 \leqslant b, k \leqslant 300?b,k?30

对于 t_2 = 2t

2

? =2 的测试点,满足 0 \leqslant b, k \leqslant 1000?b,k?100

对于 t_2 = 3t

2

? =3 的测试点,满足 0 \leqslant b, k \leqslant n0?b,k?n

对于 t_2 = 4t

2

? =4 的测试点,满足 0 \leqslant b, k \leqslant 30n0?b,k?30n

对于 t_3 = 1t

3

? =1 的测试点,保证所有询问操作都在所有修改操作之后

对于 t_3 = 2t

3

? =2 的测试点,不保证询问操作和修改操作的先后顺序

本题共 25 个测试点,每个测试点 4 分。各个测试点的数据范围如下:

题解

先考虑暴力

我们将30位压在一起,存到一个数组中,每次加减只要找到对应位置加上,处理进位即可

但这样会T,因为进位可以是一整个数组

什么情况下会T呢?

考虑11111111111111111111111111111111111111......

我们加上一个1

变成10000000000000000000000000000000000.....

又减去一个1

变成1111111111111111111111111111111111111111.....

我们发现,如果进位的地方不是全1,可以直接加上

如果进位的地方全1,那么重置为0,往下一位继续进位

如果有一整段都为1,那么全部置为0,往第一个不全是1的位进1

所以可以写一个线段树维护

【口胡很简单。。。。。我不会说我调了一个下午

#include<iostream>
#include<cstdio>
#include<algorithm>
#define BIT (1 << 30)
#define ls (u << 1)
#define rs (u << 1 | 1)
#define LL long long int
using namespace std;
const int maxn = 1000005,maxm = 1000005,INF = BIT - 1;
inline LL read(){
    LL out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57) {if (c == ‘-‘) flag = -1; c = getchar();}
    while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - ‘0‘; c = getchar();}
    return out * flag;
}
LL n,val[4 * maxn],tag[4 * maxn];
void pd(int u){
    if (tag[u] == -1) val[ls] = val[rs] = 0,tag[ls] = tag[rs] = -1;
    if (tag[u] == 1) val[ls] = val[rs] = INF,tag[ls] = tag[rs] = 1;
    tag[u] = 0;
}
void upd(int u){
    if (val[ls] == INF && val[rs] == INF) val[u] = INF;
    else if (val[ls] == 0 && val[rs] == 0) val[u] = 0;
    else val[u] = 1;
}
int modify(int u,int l,int r,int pos,LL v){
    if (l == r){
        val[u] += v;
        if (val[u] < 0) {val[u] += BIT; return -1;}
        if (val[u] > INF) {val[u] -= BIT; return 1;}
        return 0;
    }
    pd(u);
    int mid = l + r >> 1,t;
    if (mid >= pos) t = modify(ls,l,mid,pos,v);
    else t = modify(rs,mid + 1,r,pos,v);
    upd(u);
    return t;
}
int Add(int u,int l,int r,int pos){
    if (l == r){
        val[u]++;
        if (val[u] > INF) {val[u] = 0; return true;}
        return false;
    }
    pd(u);
    int mid = l + r >> 1,t = true;
    if (l >= pos){
        if (val[ls] == INF){
            val[ls] = 0; tag[ls] = -1;
            t = Add(rs,mid + 1,r,pos); upd(u);
            return t;
        }
        t = Add(ls,l,mid,pos); upd(u);
        return t;
    }
    if (mid >= pos) t = Add(ls,l,mid,pos);
    if (t) t = Add(rs,mid + 1,r,pos);
    upd(u);
    return t;
}
int Minus(int u,int l,int r,int pos){
    if (l == r){
        val[u]--;
        if (val[u] < 0) {val[u] = INF; return true;}
        return false;
    }
    pd(u);
    int mid = l + r >> 1,t = true;
    if (l >= pos){
        if (val[ls] == 0){
            val[ls] = INF; tag[ls] = 1;
            t = Minus(rs,mid + 1,r,pos); upd(u);
            return t;
        }
        t = Minus(ls,l,mid,pos); upd(u);
        return t;
    }
    if (mid >= pos) t = Minus(ls,l,mid,pos);
    if (t) t = Minus(rs,mid + 1,r,pos);
    upd(u);
    return t;
}
int getv(int u,int l,int r,int pos,LL v){
    if (l == r) return (val[u] & (1 << v)) != 0;
    pd(u);
    int mid = l + r >> 1;
    if (mid >= pos) return getv(ls,l,mid,pos,v);
    return getv(rs,mid + 1,r,pos,v);
}
int main(){
    n = read(); read(); read(); read();
    int opt,a,b,t,pos,tt,len,flag;
    for (int i = 1; i <= n; i++){
        opt = read(); a = read();
        if (opt & 1){
            b = read(); flag = a >= 0 ? 1 : -1;
            a *= flag;
            pos = b / 30; tt = b - pos * 30; len = 30 - tt;
            t = modify(1,0,n + 5,pos,flag * ((a & ((1 << len) - 1)) << tt));
            if (t == 1) Add(1,0,n + 5,pos + 1);
            if (t == -1) Minus(1,0,n + 5,pos + 1);
            a >>= len;
            if (a){
                t = modify(1,0,n + 5,pos + 1,flag * a);
                if (t == 1) Add(1,0,n + 5,pos + 2);
                if (t == -1) Minus(1,0,n + 5,pos + 2);
            }
        }else {
            printf("%d\n",getv(1,0,n + 5,a / 30,a % 30));
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Mychael/p/8428189.html

时间: 2024-10-08 11:03:21

NOIP2017整数 【线段树】的相关文章

【BZOJ4942】[Noi2017]整数 线段树+DFS(卡过)

[BZOJ4942][Noi2017]整数 题目描述去uoj 题解:如果只有加法,那么直接暴力即可...(因为1的数量最多nlogn个) 先考虑加法,比较显然的做法就是将A二进制分解成log位,然后依次更新这log位,如果最高位依然有进位,那么找到最高位后面的第一个0,将中间的所有1变成0,那个0变成1.这个显然要用到线段树,但是复杂度是nlog2n的,肯定过不去. 于是我在考场上yy了一下,这log位是连续的,我们每次都要花费log的时间去修改一个岂不是很浪费?我们可以先在线段树上找到这段区间

noi2017 T1 整数 ——线段树

loj.ac上有  题目传送门 不过我还是把题目搬过来吧 整数(integer)[题目背景]在人类智慧的山巅,有着一台字长为 1048576 位的超级计算机,著名理论计算机科 学家 P 博士正用它进行各种研究.不幸的是,这天台风切断了电力系统,超级计算机 无法工作,而 P 博士明天就要交实验结果了,只好求助于学过 OI 的你......[题目描述] P 博士将他的计算任务抽象为对一个整数的操作. 具体来说,有一个整数 x ,一开始为 0. 接下来有 n 个操作,每个操作都是以下两种类型中的一种:

[BZOJ4942][NOI2017]整数(线段树+压位)

CCF的题经常是对于一个不是非常高级的算法或数据结构挖掘性质进行优化. 松爷的题总是充满常数优化气息,这个题也确实是在常数上做文章. 首先如果全加的话是可以直接暴力的,因为可以证明对每一位来说是均摊$O(1)$的,将a二进制分解一次. 然后将暴力想下去之后容易发现,二进制加法如果进位,肯定是找到这一位之前第一个为0的位加一,然后这两位之间的所有位都变成0(就是模拟竖式加法),减法反之. 于是这个东西就是前驱查找和区间修改,应用线段树解决,$O(n\log^{2}n)$. 但是看到极其相近的数据范

【noi2017】 整数 线段树or模拟

ORZYYB 题目大意:你需要维护一个有$3\times 10^7$个二进制位的数,有一种修改方式和一种询问方式 对这个数加上$a\times2^b$,其中$|a|≤10^9$,$b≤3\times 10^7$,保证需要维护的这个数始终非负 询问这个数第k个二进制位的值 总共有$10^6$次询问/修改操作 我们不难发现,如果只有加法操作的话,对任意一个位执行加法操作,均摊进位次数是1. 证明是显然的(我貌似之前在MC里面用红石电路模拟过二进制进位过程....) 也就是说暴力加暴力进位的复杂度是正

acwing 243. 一个简单的整数问题2 树状数组 线段树

地址 https://www.acwing.com/problem/content/description/244/ 给定一个长度为N的数列A,以及M条指令,每条指令可能是以下两种之一: 1.“C l r d”,表示把 A[l],A[l+1],…,A[r] 都加上 d. 2.“Q l r”,表示询问 数列中第 l~r 个数的和. 对于每个询问,输出一个整数表示答案. 输入格式 第一行两个整数N,M. 第二行N个整数A[i]. 接下来M行表示M条指令,每条指令的格式如题目描述所示. 输出格式 对于

BZOJ 1012: [JSOI2008]最大数maxnumber(线段树)

012: [JSOI2008]最大数maxnumber Time Limit: 3 Sec  Memory Limit: 162 MB Description 现在请求你维护一个数列,要求提供以下两种操作:1. 查询操作.语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值.限制:L不超过当前数列的长度.2. 插入操作.语法:A n 功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列

HDU 1754 I Hate It(线段树之单点更新,区间最值)

I Hate It Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 70863    Accepted Submission(s): 27424 Problem Description 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感.不管你喜不喜欢,现在需要你做的是,就是按照老师的

[bzoj3932][CQOI2015]任务查询系统-题解[主席树][权值线段树]

Description 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第Ei秒后结束(第Si秒和Ei秒任务也在运行 ),其优先级为Pi.同一时间可能有多个任务同时执行,它们的优先级可能相同,也可能不同.调度系统会经常向 查询系统询问,第Xi秒正在运行的任务中,优先级最小的Ki个任务(即将任务按照优先级从小到大排序后取前Ki个 )的优先级之和是多少.特别的,如

HDU1832 二维线段树求最值(模板)

Luck and Love Time Limit: 10000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 50 Accepted Submission(s): 20   Problem Description 世界上上最远的距离不是相隔天涯海角而是我在你面前可你却不知道我爱你                ―― 张小娴 前段日子,枫冰叶子给Wiskey做了个征婚启事,聘