Lucky Array CodeForces - 121E(线段树 单点修改 区间和查询 优化)

题意:

lucky数只由4和7组成

给n个数,m个操作,操作有两种

1.区间内每个数加上d

2.询问区间lucky数有多少个

数据保证在执行完所有区间加操作后,每个数不会超过1e4!

思路1 线段树:直接暴力的话会T

看上去是区间修改,其实lucky数的修改只能落到单点上,所以值的修改和计数的修改,都是单点修改

①如果每修改一次都判断一下是不是lucky数,会T

②如果不管是不是lucky数(可能不用修改cnt),都logn地跑到叶节点,会T

优化这两个地方 3000ms/4000ms可过

具体来说,写出1e4内lucky数的表,每次O(1)地判断是不是lucky数,

线段树只维护计数cnt,每次修改直接在原数组上修改,当计数需要改变,再用线段树的单点修改。

#include<bits/stdc++.h>
using namespace std;
#define ls (p<<1)
#define rs (ls|1)
#define mid (t[p].l+t[p].r>>1)
const int N=1e5+5;
bool vis[N];
struct node{
    int l,r,cnt;
}t[N<<2];//4倍空间了!

int a[N];
int lucky[]={4,7,44,47,74,77,444,447,474,477,744,747,774,777,4444,4447,4474,4477,4744,4747,4774,4777,7444,7447,7474,7477,7744,7747,7774,7777};
void build(int p,int l,int r){
    t[p].l=l,t[p].r=r;
    if(l==r){
        if(vis[a[l]]) t[p].cnt=1;
        return;//return!
    }
    build(ls,l,mid);
    build(rs,mid+1,r);
    t[p].cnt=t[ls].cnt+t[rs].cnt;//pushup(p);
}

void update(int p,int x,int d){
    if(t[p].l==t[p].r){
        t[p].cnt+=d;
        return;
    }
    if(x<=mid) update(ls,x,d);// l<=mid
    else update(rs,x,d); //区间没有else 单点修改是else
    t[p].cnt=t[ls].cnt+t[rs].cnt;
}

int query(int p,int l,int r){
    if(l<=t[p].l&&t[p].r<=r) return t[p].cnt;
    int res=0;
    if(l<=mid) res+=query(ls,l,r);//天呐这里打错了 mid!
    if(r>mid) res+=query(rs,l,r);
    return res;
}

int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<30;++i) vis[lucky[i]]=true;
    for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    build(1,1,n);
    char op[10];
    while(m--){
        scanf("%s",&op);
        int l,r;
        if(op[0]=='a'){
            int d;
            scanf("%d%d%d",&l,&r,&d);
            for(int i=l;i<=r;++i){
                if(!vis[a[i]]&&vis[a[i]+d]) update(1,i,1);
                else if(vis[a[i]]&&!vis[a[i]+d]) update(1,i,-1);
                a[i]+=d;
            }
        }else{
            scanf("%d%d",&l,&r);
            cout<<query(1,l,r)<<"\n";
        }
    }
}

这就是我线段树的板子了!!!

 

法二 树状数组:树状数组不加上面的第二个优化也能过 树状数组比线段树常数更小 2000ms内可过

为什么我没想到这题能用树状数组呢?

树状数组是统计前缀和

这里的cnt就是可以被统计的前缀和

凡是用线段树维护的sum

都可用树状数组维护

是这样?

#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) ((x)&(-x))
const int N=1e5+5;
bool vis[N];
int a[N],c[N];
int lucky[]={4,7,44,47,74,77,444,447,474,477,744,747,774,777,4444,4447,4474,4477,4744,4747,4774,4777,7444,7447,7474,7477,7744,7747,7774,7777};
//返回前x个整数之和
int getsum(int x){
    int sum=0;
    for(int i=x;i>0;i-=lowbit(i)){//x>0
        sum+=c[i];
    }
    return sum;
}
//第x个整数加上v
void update(int x,int v){
    for(int i=x;i<=N;i+=lowbit(i)){
        c[i]+=v;
    }
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<30;++i) vis[lucky[i]]=true;
    for(int i=1;i<=n;++i) {
        scanf("%d",&a[i]);
        if(vis[a[i]]) update(i,1);
    }
    char op[10];
    while(m--){
        scanf("%s",&op);
        int l,r;
        if(op[0]=='a'){
            int d;
            scanf("%d%d%d",&l,&r,&d);
            for(int i=l;i<=r;++i){
                if(!vis[a[i]]&&vis[a[i]+d]) update(i,1);
                else if(vis[a[i]]&&!vis[a[i]+d]) update(i,-1);
                a[i]+=d;
            }
        }else{
            scanf("%d%d",&l,&r);
            cout<<getsum(r)-getsum(l-1)<<"\n";
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值