Counting Stars

Counting Stars

https://acm.hdu.edu.cn/showproblem.php?pid=7059

题意:

​ 给定n个数,分别为ai,题目要求支持三种操作:1、区间求和 2、区间减去lowbit(ai) 3、区间加2k,(2k<=ai<2^(k+1));

思路:

​ 假设只有前两个操作,考虑如何维护。这也是一个较为经典的问题。对于像这种数值快速递降至稳定的函数(再例如区间开根号,区间求欧拉函数之类的),我们可以考虑进行暴力修改。分析一下复杂度:

​ 首先理解x-lowbit(x)是相当于将x二进制中的最后一位1删掉。可以发现,每个数最多被操作 logn次就会变成0 。因此一共操作次数只有 nlogn次,实现方面,为了在暴力修改时忽略已经变成 的区间,我们可以在线段树上维护一个tg表示该区间是否所有数为0 , 这个可以通过线段树标记下传维护。

​ 那么考虑如何把第三个操作加进去。有了前面的思路,我们依然可以用二进制去考虑第三种操作。第三种操作相当于是x 最左边的1左移一格。容易发现第三种操作仅与x的最高位有关,与后面的位无关。所以容易想到把x的最高位和其他位置分开维护。那么第三种操作就相当于区间对最高位*2 , 这个也是可以使用线段树维护的。

代码:

#include <bits/stdc++.h>
using namespace std;
//#define int long long
typedef long long ll;
const int N=1e5+5;
const ll mod=998244353;
struct node{
	ll suml,sumr,tg,lay;//suml存储最高位1的值,sumr存储减去suml的值,tg标记是当前值否变为0了,lay表示乘 
}tr[N<<2];
ll a1[N],a2[N],a[N];
ll lowbit(int x){
	return x&(-x);
}
void push_up(int root){
	tr[root].suml=(long long)(tr[root<<1].suml+tr[root<<1|1].suml)%mod;
	tr[root].sumr=(long long)(tr[root<<1].sumr+tr[root<<1|1].sumr)%mod;
	tr[root].tg=tr[root<<1].tg&tr[root<<1|1].tg;//是否左右区间都为0
}
void bt(int root,int l,int r){
	tr[root].lay=1;
	tr[root].tg=0;
	if(l==r){
		tr[root].suml=(1ll<<(31-__builtin_clz(a[l])));//clz求31位的前导0个数; 
        tr[root].sumr=a[l]-tr[root].suml;
		return;
	} 
	int m=(l+r)>>1;
	bt(root<<1,l,m);
	bt(root<<1|1,m+1,r); 
	push_up(root);
}
void push_down(int root){
	tr[root<<1].suml=(long long)(tr[root<<1].suml*tr[root].lay)%mod;
	tr[root<<1].lay=(long long)(tr[root<<1].lay*tr[root].lay)%mod;
	tr[root<<1|1].suml=(long long)(tr[root<<1|1].suml*tr[root].lay)%mod;
	tr[root<<1|1].lay=(long long)(tr[root<<1|1].lay*tr[root].lay)%mod;
	tr[root<<1].tg|=tr[root].tg;
	tr[root<<1|1].tg|=tr[root].tg;
	if(tr[root<<1].tg)  tr[root<<1].sumr=0;
	if(tr[root<<1|1].tg)  tr[root<<1|1].sumr=0;
	tr[root].lay=1;
	return;
}
void up1(int root,int stdl,int stdr,int l,int r){
	if(r<stdl||l>stdr||tr[root].tg){
        return;
    }
	if(stdl==stdr){
		if(tr[root].sumr==0){
			tr[root].suml=tr[root].sumr=0;
			tr[root].tg=1;//标记为当前数已经变为0了 
		}
		else{
			tr[root].sumr-=lowbit(tr[root].sumr);
		}
		return;
	}
	push_down(root);
	int m=(stdl+stdr)>>1;
	up1(root<<1,stdl,m,l,r);
	up1(root<<1|1,m+1,stdr,l,r);
	push_up(root);
}
void up2(int root,int stdl,int stdr,int l,int r){//第二种操作最高位*2; 
	if(r<stdl||l>stdr){
        return;
    }
	if(l<=stdl&&stdr<=r){
		tr[root].suml=(tr[root].suml*2)%mod;
		tr[root].lay=(tr[root].lay*2)%mod;
		return;
	}
	push_down(root);
	int m=(stdl+stdr)>>1;
	up2(root<<1,stdl,m,l,r);
	up2((root<<1)+1,m+1,stdr,l,r);
	push_up(root);
}
ll query(int root,int stdl,int stdr,int l,int r){
    if(r<stdl||l>stdr){
        return 0;
    }
    if(l<=stdl&&r>=stdr){
        return (tr[root].suml+tr[root].sumr)%mod;
    }
    push_down(root);
    int m=(stdl+stdr)>>1;
    ll ans=0;
    ans+=query(root<<1,stdl,m,l,r)%mod;
    ans+=query(root<<1|1,m+1,stdr,l,r)%mod;
    return (ans)%mod;
}
int main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int n,m,t;
    scanf("%d",&t);
    while(t--){
    	scanf("%d",&n);
		for(int i=1;i<=n;i++){
			ll x;
			scanf("%lld",&a[i]);
		} 
		bt(1,1,n);
		scanf("%d",&m);
		while(m--){
			int k,x,y;
			scanf("%d %d %d",&k,&x,&y);
			if(k==1){
				ll ans=query(1,1,n,x,y);
				printf("%lld\n",ans);
			}
			else if(k==2){//第一种操作 
				up1(1,1,n,x,y);
			}
			else if(k==3){//第二种操作 
				up2(1,1,n,x,y);
			}
		}
	}
    return 0;
}
posted @ 2021-08-13 10:38  Curry_BP  阅读(59)  评论(0)    收藏  举报