Bob‘s Poor Math(字典树:Trie)

链接:https://ac.nowcoder.com/acm/problem/202301
来源:牛客网
 

题目描述

Bob's math is really poor. He even doesn't know how does `carry' works in plus operation. In his world, the `plus' operation performed like this : 9+5=4{9+5=4}9+5=4, 99+99=88{99+99=88}99+99=88, 5876+5576=342{5876+5576=342}5876+5576=342, 5555+5555=0{5555+5555=0}5555+5555=0, 1503+2503=3006{1503+2503=3006}1503+2503=3006, 512+1314=1826{512 + 1314 = 1826}512+1314=1826. Oh how poor his math is and he could even be the problem setter. Tragedy.

In his world, there is a `Wish' data structure. Let's see how it works:

  •  [*.] Initially, there will be a list of numbers to initialize the data structure. After initialization, game begins:
  • [1.] You may add a number to his data structure.

    Add xAdd\ xAdd x

  • [2.] You may divide all the numbers in the data structure by 10{10}10. (Note: it is integer division, for example, 9/10=0{9 / 10 = 0}9/10=0, 99/10=9{99 / 10 = 9}99/10=9, 5876/10=587{5876 / 10 = 587}5876/10=587)

    

  • [3.] You may query the largest result by given x{x}x `plus' any number from his data structure.

    Query xQuery\ xQuery x

  • [4.] You may query `sum' ( by his poor math ) of numbers between L{L}L and R{R}R inclusive. It means, you need to find all numbers x{x}x in his data structure that can meet L≤x≤RL \leq x \leq RL≤x≤R, and conduct the `plus' one by one to get the `sum'.

    Sum L RSum\ L\ RSum L R

Can you help naive Bob to implement data struct `Wish'?

输入描述:

At very beginning, there would be a number T\mathbf{T}T indicates the number of tests. ( 1≤T≤1001 \leq \mathbf{T} \leq 1001≤T≤100 )
For each test case:
The first line contains two integers N{N}N and M{M}M indicates the number of initial numbers and number of operstions. (1≤N≤10,0001 \leq \mathbf{N} \leq 10,0001≤N≤10,000 and 1≤M≤10,0001 \leq \mathbf{M} \leq 10,0001≤M≤10,000)
The second line contains N{N}N integers for the initial numbers aia_iai​. (0≤ai≤2×109)(0 \leq a_i \leq 2 \times 10^9 )(0≤ai​≤2×109) 
The following M{M}M lines would follow the operation format #1\#1#1 to #4\#4#4.
For the operations, 0≤L≤R≤2×1090 \leq L \leq R \leq 2 \times 10^90≤L≤R≤2×109 , 0≤x≤2×1090 \leq x \leq 2 \times 10^90≤x≤2×109

输出描述:

For each test case, output one line containing "Case #x:", where x is the test case number (starting from 1).
For each operation Query{Query}Query and Sum{Sum}Sum, output the answer

示例1

输入

复制1 4 5 1 2 88 29 Add 44 Query 71 Shift Sum 0 10 Query 91

1
4 5
1 2 88 29
Add 44
Query 71
Shift
Sum 0 10
Query 91

输出

复制Case #1: 90 4 99

Case #1:
90
4
99

说明

For Sample Case #1:
Initially: [1,2,29,88]{[1 , 2 , 29 , 88]}[1,2,29,88]
Add 44: [1,2,29,88,44]{[1 , 2 , 29 , 88 , 44]}[1,2,29,88,44]
Query 71: 29+71=90{29+71=90}29+71=90; 88+71=59{88+71=59}88+71=59; 44+71=15{44+71=15}44+71=15
Shift:[0,0,2,8,4]{ [0 , 0 , 2 , 8 , 4]}[0,0,2,8,4]
Sum 0 10: 0+0+2+8+4=4{0 + 0 + 2 + 8 + 4 = 4}0+0+2+8+4=4
Query 91: 91+8=99{91 + 8=99}91+8=99

题意:

bob的数学很差,所以在他的加法运算法则中没有进位,所以a+b的结果即为对应位数字相加%10的结果作为结果当前位的数值,本题的加法采用这种原则,给定四种操作:

add:向集合中添加一个数

query:查询当前集合每个数加x后的最大值

sum:查询数值在[L,R]的所有数之和

shift:将集合中所有数都统一/10

思路:

因为数据比较多,所以可以采用字典树(Trie)保存所有数字,即:每个数字按位(从高位到低位)逐位添加,每一位对应字典树的一个结点。

sum和tag分别为树维护的数值和表示累计/10次数的懒标记。

add:直接把数字按位插入到字典树即可

query:遍历每一层,从大到小枚举,找到前结果当前数位的最大值,并根据结果当前位数值和一个加数当前位数值查看字典树中有无对应另一个加数的对应结点,若有,则说明当前结果位的当前数值j可取。最后统计出来即可。

sum:类似线段树的区间查询(从高位到低位逐层枚举),每一层 乘 位权后即可得到下一层范围。

shift:新建一个树根结点并保存/10后的结果,同时更新sum和tag。

代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>

#define int long long

using namespace std;

const int maxn=2e5+5;

int son[maxn][10],idx,rt,n,m,power[11];
int sum[maxn],tag[maxn];
string op;

void init()
{
    for(int i=1;i<=idx;i++){
        memset(son[i],0,sizeof son[i]);
        sum[i]=tag[i]=0;
    }
    rt=idx=1;
}

void add(int &x,int y)
{
    if(!x||!y){
        x=x|y;
        return;
    }
    int res=0;
    for(int i=0;i<=9;i++,x/=10,y/=10) res+=(x%10+y%10)%10*power[i];
    x=res;
}

void pushdown(int p)
{
    if(!tag[p]) return;
    for(int i=0;i<=9;i++){
        if(!son[p][i]) continue;
        int x=son[p][i];
        if(tag[p]>10) sum[x]=0;
        else sum[x]/=power[tag[p]],tag[x]+=tag[p];
    }
    tag[p]=0;
}

void insert(int x)
{
    int p=rt;
    add(sum[p],x);
    for(int i=9;i>=0;i--){
        pushdown(p);
        int y=x/power[i]%10;
        if(!son[p][y]) son[p][y]=++idx;
        p=son[p][y];
        add(sum[p],x);
    }
}

void shift()
{
    son[++idx][0]=rt;
    sum[idx]=sum[rt]/10;
    tag[rt=idx]++;
}

int query(int x)
{
    int p=rt,res=0;
    for(int i=9;i>=0;i--){
        pushdown(p);
        int w=x/power[i]%10,j=9;
        while(!son[p][(j-w+10)%10]) j--;
        res+=j*power[i];
        p=son[p][(j-w+10)%10];
    }
    return res;
}

int query_sum(int p,int dep,int l,int r,int L,int R)
{
    if(l>R||r<L) return 0;
    if(l>=L&&r<=R) return sum[p];
    pushdown(p);
    int res=0;
    for(int i=0;i<=9;i++){
        if(!son[p][i]) continue;
        int nl=l+i*power[dep],nr=l+(i+1)*power[dep]-1;
        add(res,query_sum(son[p][i],dep-1,nl,nr,L,R));
    }
    return res;
}

signed main()
{
    power[0]=1;
    for(int i=1;i<=10;i++) power[i]=power[i-1]*10;
    int t;
    cin>>t;
    for(int i=1;i<=t;i++){
        cout<<"Case #"<<i<<":"<<endl;
        cin>>n>>m;
        init();
        while(n--){
            int x;
            cin>>x;
            insert(x);
        }
        while(m--){
            cin>>op;
            if(op=="Shift") shift();
            else if(op=="Sum"){
                int l,r;
                cin>>l>>r;
                cout<<query_sum(rt,9,0,power[10]-1,l,r)<<endl;
            }
            else{
                int x;
                cin>>x;
                if(op=="Add") insert(x);
                else cout<<query(x)<<endl;
            }
        }
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值