codeforces problem 1217 E Sum Queries?(线段树)

链接

题意:

当集合中有某两个元素的十进制表示在某一位均不为0,就认为这是一个不平衡的multiset。
现在给出一个数组,每次单点修改一个值,查询某个区间所有的数能组成的元素和最小的不平衡的multiset。


思路:

最小的multiset肯定是由两个数构成。由于十进制上的每一位都是相互独立的,那么就对每一位做一棵线段树,维护区间前两小即可,如果某个数字在某一位上是0,就记为inf,否则就记为该数本身。


参考代码:
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
const int INF=2e9+5;
int a[N];

int tree[10][N<<2][2];
int ten[10]={
        1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000
};

int cal(int x,int id){//某个数在某个数位上是0就返回inf
    int tp=x/ten[id];
    if(tp%10){
        return x;
    }
    else{
        return INF;
    }
}

#define lson rt<<1
#define rson rt<<1|1


struct node{
    int minn[2];
    node(){
        this->minn[0]=this->minn[1]=INF;
    }
    node(int a,int b){
        this->minn[0]=a;
        this->minn[1]=b;
    }
};

node cal2(node a,node b){
    node ret;
    int sz=0;
    for(int t1=0,t2=0;t1<=1&&t2<=1;){
        if(sz==2)break;
        if(a.minn[t1]<=b.minn[t2]){
            ret.minn[sz++]=a.minn[t1];
            t1++;
        }
        else{
            ret.minn[sz++]=b.minn[t2];
            t2++;
        }
    }
    return ret;
}

void push_up(int id,int rt){
    node a=node(tree[id][lson][0],tree[id][lson][1]);
    node b=node(tree[id][rson][0],tree[id][rson][1]);
    node c=cal2(a,b);
    tree[id][rt][0]=c.minn[0];
    tree[id][rt][1]=c.minn[1];
}

void build(int id,int rt,int l,int r){
    if(l==r){
        tree[id][rt][0]=cal(a[l],id);
        tree[id][rt][1]=INF;
        return;
    }
    int mid=(l+r)>>1;
    build(id,lson,l,mid);
    build(id,rson,mid+1,r);
    push_up(id,rt);
}

void update(int id,int rt,int l,int r,int x,int v){
    if(l==r){
        tree[id][rt][0]=cal(v,id);
        tree[id][rt][1]=INF;
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid)update(id,lson,l,mid,x,v);
    else update(id,rson,mid+1,r,x,v);
    push_up(id,rt);
}

node query(int id,int rt,int l,int r,int x,int y){
    if(x<=l&&r<=y){
        node ret;
        ret.minn[0]=tree[id][rt][0];
        ret.minn[1]=tree[id][rt][1];
        return ret;
    }
    int mid=(l+r)>>1;
    if(y<=mid)return query(id,lson,l,mid,x,y);
    else if(x>mid)return query(id,rson,mid+1,r,x,y);
    else return cal2(query(id,lson,l,mid,x,mid),query(id,rson,mid+1,r,mid+1,y));
}

int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }

    for(int i=0;i<10;i++){
        build(i,1,1,n);
    }

    int op,x,y;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&op,&x,&y);
        if(op==1){
            for(int j=0;j<10;j++){
                update(j,1,1,n,x,y);
            }
        }
        else{
            int ans=INF;
            for(int j=0;j<10;j++){
                node tp=query(j,1,1,n,x,y);
//                printf("%d %d\n",tp.minn[0],tp.minn[1]);
                int tmp;
                if(tp.minn[1]==INF){//第二小是inf说明只有一个数在该位置上不为0
                    tmp=INF;
                }
                else{
                    tmp=tp.minn[0]+tp.minn[1];
                }
                ans=min(ans,tmp);
            }
            printf("%d\n",ans==INF?-1:ans);
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值