zoukankan      html  css  js  c++  java
  • BZOJ.4942.[NOI2017]整数(分块)

    BZOJ
    洛谷
    UOJ

    可能是退役之前最后一个BZOJ rank1了?


    参考这里

    如果没有减法,对一个二进制数暴力进位,均摊复杂度是(O(1))的(要进(O(n))次位就至少需要(O(n))次操作)。
    但是这题有减法...显然暴力进位就不对了。
    那么我们把减法变成加法,分别维护加上的数(inc)和减掉的数(dec)是多大。查询时显然不能直接两位相减,要判断一下后面是否需要进位。
    对此用(set)维护一下(inc,dec)所有不同位的位置,找到查询位后面第一个不同的位置,判一下大小关系就可以了。

    关于维护进位,比较显然的是拿线段树维护每一位的情况,把(a imes2^b)拆成((2^{a_1}+2^{a_2}+...)2^n)。。这样就成两个(log)了(╯‵□′)╯︵┻━┻。
    注意到(a)不算大,而且线段树的每个位置是可以表示(16)(32)位的。直接把(a)左移(b)位(本来就是= =),也就是把(a)加到(b)那个位置即可。如果取(32)位这么加一次显然是只会进位一次的(最多影响两个位置)。
    (32)位可以直接用unsigned int,自然溢出就可以得到加之后这几位的值,判断是否进位就判一下这个数加之前与加之后的大小关系即可)

    注意到这个线段树其实没什么必要。分块,每块维护(32)位的值,每次只要在对应块上加,然后暴力进位即可。
    复杂度(O(nlog n))set...)。

    另外移位不能(geq)位宽,所以移(32)位拆成移(31)位再移(1)位好了。。


    //18148kb	3740ms
    #include <set>
    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    #define pc putchar
    #define gc() getchar()
    #define MAXIN 300000
    //#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
    typedef long long LL;
    typedef unsigned int uint;
    const int N=1e6+5;
    
    uint inc[N],dec[N];
    std::set<int> st;
    char IN[MAXIN],*SS=IN,*TT=IN,OUT[N<<1],*O=OUT;
    
    inline int read()
    {
    	int now=0,f=1;register char c=gc();
    	for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
    	for(;isdigit(c);now=now*10+c-48,c=gc());
    	return now*f;
    }
    void Modify(uint a,uint b,uint *A,uint *B)//a在传参前就要取abs啊(uint)
    {//2^5=32
    	int p=b>>5,q=b&31;
    	uint tmp=A[p],delta=(a>>31-q)>>1; A[p]+=(a<<q);//uint
    	delta+=(A[p]<tmp); std::set<int>::iterator it;
    	if(A[p]!=B[p]) st.insert(p);
    	else if((it=st.find(p))!=st.end()) st.erase(it);
    	while(delta)
    	{
    		tmp=A[++p], A[p]+=delta, delta=A[p]<tmp;
    		if(A[p]!=B[p]) st.insert(p);
    		else if((it=st.find(p))!=st.end()) st.erase(it);
    	}
    }
    void Query()
    {
    	int b=read(),p=b>>5,q=b&31,ans=((inc[p]^dec[p])>>q)&1;
    	uint v1=inc[p]&((1<<q)-1),v2=dec[p]&((1<<q)-1);//inc[p]%(2^q) 取出该块p后面的部分 
    	if(v1<v2) *O++=((ans^1)+48);//借位 
    	else if(v1>v2||/*st.empty()||*/*st.begin()>=p) *O++=(ans+48);
    	else
    	{
    		std::set<int>::iterator it=st.lower_bound(p); --it;
    		*O++=((inc[*it]>dec[*it]?ans:ans^1)+48);
    	}
    	*O++='
    ';
    }
    
    int main()
    {
    	st.insert(N);
    	for(int T=read(),a=(read(),read(),read()),b; T--; )
    		switch(read())
    		{
    			case 1: a=read(),b=read(),Modify(a>0?a:-a,b,a>0?inc:dec,a>0?dec:inc); break;
    			case 2: Query();
    		}
    	fwrite(OUT,1,O-OUT,stdout);
    
    	return 0;
    }
    
  • 相关阅读:
    课后总结
    构建之法阅读笔记01
    软件工程周总结02
    开课博客
    二维数组最大子数组和
    大二下周总结四
    大二下周总结三
    定义一个整型数组,返回该数组中子数组和的最大值
    软件工程开课
    定义一个数组返回最大子数组的值(1)
  • 原文地址:https://www.cnblogs.com/SovietPower/p/10634895.html
Copyright © 2011-2022 走看看