Sonya and Queries CodeForces - 713A [字典树] 好题~

A. Sonya and Queries
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

Today Sonya learned about long integers and invited all her friends to share the fun. Sonya has an initially empty multiset with integers. Friends give her t queries, each of one of the following type:

  1.  +  ai — add non-negative integer ai to the multiset. Note, that she has a multiset, thus there may be many occurrences of the same integer.
  2.  -  ai — delete a single occurrence of non-negative integer ai from the multiset. It's guaranteed, that there is at least one ai in the multiset.
  3. ? s — count the number of integers in the multiset (with repetitions) that match some pattern s consisting of 0 and 1. In the pattern, 0stands for the even digits, while 1 stands for the odd. Integer x matches the pattern s, if the parity of the i-th from the right digit in decimal notation matches the i-th from the right digit of the pattern. If the pattern is shorter than this integer, it's supplemented with 0-s from the left. Similarly, if the integer is shorter than the pattern its decimal notation is supplemented with the 0-s from the left.

For example, if the pattern is s = 010, than integers 92221250 and 414 match the pattern, while integers 311025 and 1030 do not.

Input

The first line of the input contains an integer t (1 ≤ t ≤ 100 000) — the number of operation Sonya has to perform.

Next t lines provide the descriptions of the queries in order they appear in the input file. The i-th row starts with a character ci — the type of the corresponding operation. If ci is equal to '+' or '-' then it's followed by a space and an integer ai (0 ≤ ai < 1018) given without leading zeroes (unless it's 0). If ci equals '?' then it's followed by a space and a sequence of zeroes and onse, giving the pattern of length no more than 18.

It's guaranteed that there will be at least one query of type '?'.

It's guaranteed that any time some integer is removed from the multiset, there will be at least one occurrence of this integer in it.

Output

For each query of the third type print the number of integers matching the given pattern. Each integer is counted as many times, as it appears in the multiset at this moment of time.

Examples
input
Copy
12
+ 1
+ 241
? 1
+ 361
- 241
? 0101
+ 101
? 101
- 101
? 101
+ 4000
? 0
output
2
1
2
1
1
input
Copy
4
+ 200
+ 200
- 200
? 0
output
1
Note

Consider the integers matching the patterns from the queries of the third type. Queries are numbered in the order they appear in the input.

  1. 1 and 241.
  2. 361.
  3. 101 and 361.
  4. 361.
  5. 4000.

题意:

1. + a代表把数字a存到字典中

2. - a代表把数字b存到字典中

3. ? a代表与a奇偶性相同的有多少个

其中中判断奇偶性的时候,可以往左边边加0


思路:

1,2两个操作很容易,用字典树去维护。

重点第3个操作。要求奇偶性,那么我们在1,2操作中仅仅从右到左保存其每一位数字的奇偶性,不存数字,方便第3次操作的判断

如何判断?

1.一层一层搜18次就可以,如果途中不能匹配,说明没有这个奇偶性的情况,return 0。 如果可以18次,说明匹配成功,返回cnt[维护到当前这个节点的单词有多少个]

2.为什么是18次?数最大为1e18,最多为18位,那么对于我在左边加0到18位能够完美解决题目的要求。如果我按max(len1,len2)去做,非常的复杂。 何不都加到18位呢,如果当前位置是1,而在字典中当前位置是1的没有,肯定没办法匹配了。 如果当前位置是0,如果这个0不是补的,匹配则pass,不匹配就return 0了。如果这个0是补的,而字典中为0的没有,那么肯定return 0了。

3.不管是不是补的,因为往左边补0到长度相同是题目给的条件,那么我们补充18-max(len1,len2)补充0完整了,如果前面max(len1,len2)都匹配了[事实上这段是我们真正要匹配的],后面的18-max(len1,len2)个0肯定是匹配的。全部弄成18位,方便其他max(len1,len2)不同的串来比较!!!

举个例子

1111110

10->0000010

如果我们这样维护字典树一次。假如再进来一个串10,我们如何去匹配呢,是不是我要一直往左边加0,搜一次,再加0,再搜一次。这样复杂度就很高了。

所以我们统一了18位,就可以在O(18*t)的复杂度解决问题了。

有一点要注意的是cnt何时加,何时减的问题:

1.如果cnt在每次for循环都加,这样就联系了前缀和完整数的关系.

2.如果cnt放在for循环外面,这样只有最后一位的cnt=1.

对于这题,两种方法无所谓,都一样.但对于下一篇文章,有一种方法就错误了.

为什么对于这题两种方法都是正确的呢?

因为这题要求的是奇偶性一样,就算当前这个数已经删完了,但它最后的cnt=0,return 的是0 不影响答案了.

所以还是放for循环里面更好理解~

#include <stdio.h>
#include <malloc.h>
#include <iostream>
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define bug1 cout << "bug1" << endl
#define bug2 cout << "bug2" << endl
#define bug3 cout << "bug3" << endl
#define bug4 cout << "bug4" << endl
#pragma comment(linker, "/STACK:102400000,102400000")


using namespace std;
typedef long long ll;

const int MAX_N=1e5+5;
struct node{
    node *next[2];
    int cnt;
    node(){
        cnt=0;
        next[0]=next[1]=NULL;
    }
};

node *root;
void buildtrie(ll x){
    node *p=root;
    for(int i=1;i<=18;i++){
        int t=x%10%2;
        if(!p->next[t]) p->next[t]=new node();
        p=p->next[t];
        x/=10;p->cnt++;
    }
    
    return ;
}

void del(ll x){
    node *p=root;
    for(int i=1;i<=18;i++){
        int t=x%10%2;
        p=p->next[t];
        x/=10;p->cnt--;
    }
    
    return ;
}

int query(ll x){
    node *p=root;
    for(int i=1;i<=18;i++){
        int t=x%10%2;
        if(!p->next[t]) return 0;
        p=p->next[t];
        x/=10;
    }
    return p->cnt;
}

int main(void){
    int t;
    root=new node();
    cin >> t;
    for(int i=1;i<=t;i++){
        char op[2];
        ll num;
        scanf("%s",op+1);
        scanf("%I64d",&num);
        if(op[1]=='+')  buildtrie(num);
        else if(op[1]=='-') del(num);
        else if(op[1]=='?') printf("%d\n",query(num));
    }
    return 0;
}
/*
*/


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值