• P4428-[BJOI2018]二进制【树状数组,set】


    正题

    题目链接:https://www.luogu.com.cn/problem/P4428


    题目大意

    长度为(n)(0/1)串要求支持

    1. 修改一个位置
    2. 求区间([l,r])有多少个子区间重排后的二进制数可以被三整除

    (1leq nleq 10^5)


    解题思路

    首先有(2^{2k}\%3=1(kin Z))(2^{2k+1}\%3=2(kin Z))
    分三种情况考虑

    • (1)(1)那么显然无论如何都不可以被三整除
    • (2k)(1)那么我们之间都排在最后面就好了。
    • (2k+1)(1)(k)不能为(0)),那么有一种方案就是把某个在奇数位置的(1)放到偶数位置就可以了,此时需要区间的长度至少为(2k+3)

    然后具体分析一下相当于一个区间(1)的个数不能为(1)且如果是奇数个那么必须至少有两个(0)

    看起来很复杂可以反过来做分成以下情况

    1. 区间全是(1)且长度为奇数
    2. 区间有一个(0)且长度为偶数
    3. 区间只有一个(1)
    4. 由于(2)(3)会重复一种只有一个(1)和一个(0)的情况所以需要加回这个方案

    第四种是最好维护的,顺便用树状数组记录就好了

    然后前三种我们对于(0/1)的位置分别开一个(set)来查询某个位置前驱/后继的0/1。

    然后第三种情况我们对于每个(1)考虑左右的(0)区间然后记录在树状数组(1)的位置

    对于第二种情况我们考虑对于每个(0)考虑左右的(1)然后记录在那个(0)的位置

    对于第一种情况我们之间记录到区间最左端的(0)处。

    然后统计答案的时候要记得把边界的情况考虑

    写起来有点麻烦

    时间复杂度(O(nlog n))


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<set>
    #define lowbit(x) (x&-x)
    #define ll long long
    using namespace std;
    const ll N=1e5+10;
    ll n,m,a[N],t[N],p[N];
    set<ll> s[2];
    void Change(ll x,ll val){
    	while(x<=n){
    		t[x]+=val;
    		x+=lowbit(x);
    	}
    	return;
    }
    ll Ask(ll x){
    	ll ans=0;
    	while(x){
    		ans+=t[x];
    		x-=lowbit(x);
    	}
    	return ans;
    }
    ll Left(ll op,ll x)
    {return (*--s[op].upper_bound(x));}
    ll Right(ll op,ll x)
    {return (*s[op].lower_bound(x));}
    ll Count(ll n)
    {return (n+1)/2*(n+2-(n&1))/2;}
    ll Caunt(ll n)
    {return n*(n+1)/2;}
    ll Calc(ll L,ll R)
    {return (L/2+1)*((R+1)/2)+((L+1)/2)*(R/2+1);}
    void Updata(ll x){
    	if(x<1||x>n)return;
    	if(p[x])Change(x,-p[x]);
    	if(a[x]){
    		ll L=(x-Left(1,x-1)-1),R=(Right(1,x+1)-x-1);
    		p[x]=(L+1)*(R+1)-1;
    	}
    	else{
    		ll L=(x-Left(0,x-1)-1),R=(Right(0,x+1)-x-1);
    		p[x]=Calc(L,R)+Count(R);
    	}
    	if(x<n&&a[x]!=a[x+1])p[x]--;
    	Change(x,p[x]);
    	return;
    }
    ll Get(ll x,ll l,ll r){
    	ll L=max(Left(0,x-1),l-1),R=min(Right(0,x+1),r+1);
    	L=x-L-1;R=R-x-1;
    	return Calc(L,R);
    }
    ll Qet(ll x,ll l,ll r){
    	ll L=max(Left(1,x-1),l-1),R=min(Right(1,x+1),r+1);
    	L=x-L-1;R=R-x-1;
    	return (L+1)*(R+1)-1;
    }
    signed main()
    {
    	scanf("%lld",&n);
    	s[0].insert(0);s[0].insert(n+1);
    	s[1].insert(0);s[1].insert(n+1);
    	for(ll i=1;i<=n;i++)
    		scanf("%lld",&a[i]),s[a[i]].insert(i);
    	for(ll i=1;i<=n;i++)
    		Updata(i);
    	scanf("%lld",&m);
    	while(m--){
    		ll op,l,r,x;
    		scanf("%lld",&op);
    		if(op==1){
    			scanf("%lld",&x);
    			s[a[x]].erase(x);
    			a[x]=!a[x];
    			s[a[x]].insert(x);
    			Updata(x);
    			Updata(Left(0,x-1));
    			Updata(Left(1,x-1));
    			Updata(Right(0,x+1));
    			Updata(Right(1,x+1));
    		}
    		else{
    			scanf("%lld%lld",&l,&r);
    			ll ans=(r-l+1)*(r-l+2)/2;
    			if(Left(1,r)<l){printf("%lld
    ",ans);continue;}
    			if(Left(0,r)<l){ans-=Count(r-l+1);printf("%lld
    ",ans);continue;}
    			ans-=Ask(r)-Ask(l-1);
    			if(r<n&&a[r]!=a[r+1])ans--;
    			ll Ll=Left(0,l-1),Rr=Right(0,r+1),Lr=Left(0,r),Rl=Right(0,l);
    			ans=ans+Get(Rl,1,n)-Get(Rl,l,r);
    			if(Lr!=Rl)ans=ans+Get(Lr,1,n)-Get(Lr,l,r);
    			if(a[r+1])ans=ans+Count(Rr-Lr-1)-Count(r-Lr);
    			if(a[l])ans=ans-Count(Rl-l);
    			
    			Ll=Left(1,l),Rr=Right(1,r),Lr=Left(1,r),Rl=Right(1,l);
    			ans=ans+Qet(Rl,1,n)-Qet(Rl,l,r);
    			if(Lr!=Rl)ans=ans+Qet(Lr,1,n)-Qet(Lr,l,r);
    //			if(!a[r])ans=ans+Caunt(Rr-Rl-1)-Caunt(r-Rl);
    //			if(!a[l])ans=ans-Caunt(Lr-l);
    			
    			printf("%lld
    ",ans);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    《梦断代码》阅读笔记Ⅰ
    BICEP单元测试计划——四则运算Ⅱ
    软件工程随堂小作业——随机四则运算Ⅱ(C++)
    PSP0级 周活动总结表+时间记录日志+缺陷记录日志 表格模板
    阅读《软件工程—理论方法与实践》第十章心得体会
    阅读《软件工程—理论方法与实践》第九章心得体会
    阅读《软件工程—理论方法与实践》第八章心得体会
    阅读《软件工程—理论方法与实践》第七章心得体会
    阅读《软件工程—理论方法与实践》第六章心得体会
    阅读《软件工程—理论方法与实践》第五章心得体会
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/15002436.html
Copyright © 2020-2023  润新知