Lowbit

题目链接

题意:输入给出包含n个数的数组,和m次询问,询问包含两种,1、对区间[l,r]中的每一位数都加上一个lowbit(ai),2、询问[l,r]中数组和

思路:这题不难想到是线段树,考虑如何实现区间+lowbit操作,lowbit是一个数中最小的一位1,形式是2的n次方,我们对一个二进制进行+lowbit模拟不难发现,一个数在经过不超过log(n)次操作后,其二进制形式中就只剩下了一个1,在此之后进行的+lowbit操作就全变成了*2,所以我们考虑在使用线段树的时候对每个区间加一个pd判断,如果当前区间内的所有数都已经变成了只有一位1,在此之后对该区间进行操作1,就可以直接进行区间乘法,注意题目要求最后结果模上一个数,而一个数在变成只有1个1之前不能取模,因为这样会影响后续判断,单独对一个数进行+lowbit操作不能对整个区间操作,所以需要一直推到线段树最底层然后逐层返回状态,这样是很耗费时间的,所以我们要使每个数尽可能快的变为只有1个1的状态

ac代码:

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define INF 0x3f3f3f3f
#define pb push_back
#define int long long
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define Mirai ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
using namespace std;
typedef pair<int,int> pii;
const int N=1e5+10,mod=998244353;
struct Node
{
    int l,r;
    int sum,lazy,pd;
}tr[N<<2];
//此处pd用来标记此区间的所有数是否已经只剩一位1
//如果只剩一位1即可直接用lazy进行区间乘法
int w[N];
int op,l,r,q,n;
int lowbit(int x)
{
    return x&-x;
}
void change(int u,int lazy)
{
    tr[u].sum=((tr[u].sum%mod)*(lazy%mod))%mod;
    tr[u].lazy=((tr[u].lazy%mod)*(lazy%mod))%mod;
}
void pushup(int u)
{
    tr[u].sum=((tr[u<<1].sum%mod)+(tr[u<<1|1].sum%mod)+mod)%mod;
    tr[u].pd=tr[u<<1].pd&tr[u<<1|1].pd;//只有左右区间pd都为1,父区间pd才能为1
}
void pushdown(int u)
{
    if(tr[u].lazy>1)
    {
        change(u<<1,tr[u].lazy);
        change(u<<1|1,tr[u].lazy);
        tr[u].lazy=1;
    }
}
void build(int u,int l,int r)
{
    if(l==r)
    {
        tr[u]={l,r,w[l],1,0};
        if(w[l]==lowbit(w[l]))tr[u].pd=1;//建立线段树的时候注意预先判断pd条件
    }
    else 
    {
        tr[u]={l,r};
        tr[u].lazy=1;
        int mid=l+r>>1;
        build(u<<1,l,mid);
        build(u<<1|1,mid+1,r);
        pushup(u);
    }
}
void modify(int u,int l,int r)
{
    if(tr[u].l>=l&&tr[u].r<=r&&tr[u].pd==1)//如果区间pd==1则可以直接去加乘2
    {
        change(u,2);
        return ;
    }
    if(tr[u].l==tr[u].r)//如果一直找到了最底层,则进行+lowbit操作然后逐层返回状态
    {
        tr[u].sum=tr[u].sum+lowbit(tr[u].sum);//注意此处不能%mod
        if(tr[u].sum==lowbit(tr[u].sum))tr[u].pd=1;//操作结束后判断一次该数是否满足只有一位1的条件,然后更新状态
        return ;
    }
    pushdown(u);
    int mid=tr[u].l+tr[u].r>>1;
    if(l<=mid)modify(u<<1,l,r);
    if(r>mid)modify(u<<1|1,l,r);
    pushup(u);
}
int query(int u,int l,int r)
{
    if(tr[u].l>=l&&tr[u].r<=r)return tr[u].sum%mod;
    pushdown(u);
    int mid=tr[u].l+tr[u].r>>1;
    int res=0;
    if(l<=mid)res=(res%mod+query(u<<1,l,r)%mod)%mod;
    if(r>mid)res=(res%mod+query(u<<1|1,l,r)%mod)%mod;
    return res;
}
void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++)cin>>w[i];
    build(1,1,n);
    cin>>q;
    while(q--)
    {
        cin>>op>>l>>r;
        if(op==1)modify(1,l,r);
        else cout<<query(1,l,r)<<endl;
    }
}
signed main()
{
    Mirai;
    int T=1;
    cin>>T;
    while(T--)
    {
        solve();
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值