• [BZOJ4942] [NOI2017]整数


    题目背景

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

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

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

    题目描述

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

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

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

    • 1 a b:将x加上整数(acdot 2^b),其中a为一个整数,b为一个非负整数
    • 2 k :询问x在用二进制表示时,位权为2^k的位的值(即这一位上的1代表 2^k)

    保证在任何时候,(xgeqslant 0)

    输入格式

    输入的第一行包含四个正整数n,t_1,t_2,t_3,n的含义见题目描述,t_1,t_2,t_3的具体含义见子任务。

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

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

    输出格式

    对于每个询问操作,输出一行,表示该询问的答案(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
    

    Solution

    奇怪的题...

    注意到一个性质,二进制加法每次加一的均摊复杂度是(O(1))的而不是(O(log n))的,具体可以考虑每一位被加了多少次。

    那么其实我们可以压位然后暴力修改,但是均摊复杂度有一个缺点,就是不支持撤回操作,否则你可以在一次复杂度较高的操作前后跳转,然后时间复杂度就不对了。

    这题的减法操作就可以视为是撤回操作。

    那么我们可以维护两个高精度的数,分别表示一共加了多少减了多少,那么仔细思考一下就可以知道,对于询问我们唯一的瓶颈就是如何比较大小。

    注意到字符串比较大小的暴力算法,我们只需要找到第一个不同的位就好了,那么我们可以开(set)来维护不同的位置,每次修改的时候更新一下就好了。

    那么这题就变成了小清新模拟题,然后我调了一晚上

    细节是真的挺多的....不过跑的还挺快...(bzoj) (rk6)

    #include<bits/stdc++.h>
    using namespace std;
     
    void read(int &x) {
        x=0;int f=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
        for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
    }
     
    void print(int x) {
        if(x<0) putchar('-'),x=-x;
        if(!x) return ;print(x/10),putchar(x%10+48);
    }
    void write(int x) {if(!x) putchar('0');else print(x);putchar('
    ');}
    
    #define ui unsigned int 
    
    const int maxn = 1e6+10;
    
    int n,_,s[maxn];
    ui r[maxn][2];
    
    set<int > dif;
    
    int cnt;
    
    int main() {
    	read(n),read(_),read(_),read(_);
    	for(int i=1;i<=n;i++) {
    		int op,a,b;read(op),read(a);
    		if(op==1) {
    			int k=0;read(b);
    			if(a<0) a=-a,k=1;
    			int t=b/32;b&=31;
    			ui p=(ui)a<<b,q=a>>(31-b),pre;q>>=1;
    			pre=r[t][k],r[t][k]+=p,q+=r[t][k]<pre;
    			if((r[t][k]^r[t][!k])&&!s[t]) s[t]=1,dif.insert(t);
    			else if(r[t][k]==r[t][!k]&&s[t]) s[t]=0,dif.erase(t);t++;
    			while(q) {
    				pre=r[t][k];r[t][k]+=q;q=r[t][k]<pre;
    				if((r[t][k]^r[t][!k])&&!s[t]) s[t]=1,dif.insert(t);
    				else if(r[t][k]==r[t][!k]&&s[t]) s[t]=0,dif.erase(t);
    				t++;
    			}
    		} else {
    			cnt++;
    			int t=a/32;a&=31;
    			int ans=((r[t][0]>>a)^(r[t][1]>>a))&1;
    			ui x=r[t][0]&((1u<<a)-1),y=r[t][1]&((1u<<a)-1);
    			if(x<y) write(ans^1);
    			else if(x>y||dif.empty()||t<=(*dif.begin())) write(ans);
    			else {
    				set<int > :: iterator it=dif.lower_bound(t);--it;
    				if(r[*it][0]>r[*it][1]) write(ans);
    				else write(ans^1);
    			}
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    移动端开发适配总结
    gulp进阶构建项目由浅入深
    css3 实现逐帧动画
    jQuery1.9.1源码分析--数据缓存Data模块
    nodejs开发指南读后感
    css3动画由浅入深总结
    浅谈javascript函数节流
    go语言基础之copy的使用
    go语言基础之append扩容特点
    go语言基础之append函数的使用
  • 原文地址:https://www.cnblogs.com/hbyer/p/10507324.html
Copyright © 2020-2023  润新知