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;
}

浙公网安备 33010602011771号