Wannafly挑战赛14 B 前缀查询(字典树 + 0.5dfs)


题目描述 

在一个 Minecraft 村庄中,村长有这一本小写字母构成的名册(字符串的表),
每个名字旁边都记录着这位村民的声望值,而且有的村民还和别人同名。
随着时间的推移,因为没有村民死亡,这个名册变得十分大。
现在需要您来帮忙维护这个名册,支持下列 4 种操作:
1. 插入新人名 s i,声望为 a i
2. 给定名字前缀 p i 的所有人的声望值变化 d i
3. 查询名字为 s 村民们的声望值的和(因为会有重名的)
4. 查询名字前缀为 p 的声望值的和

输入描述:

第一行为两个整数 0 ≤ N ≤ 105,表示接下来有 N 个操作;
接下来 N 行,每行输入一个操作,行首为一个整数 1 ≤ oi ≤ 4,表示这一行的操作的种类,
那么这一行的操作和格式为:
1. 插入人名,这一行的格式为 1 s i a i,其中 |a i| ≤ 10 32. 前缀修改声望,这一行的格式为 2 p i d i,其中 |d i| ≤ 10 33. 查询名字的声望和,这一行的格式为 3 s j4. 查询前缀的声望和,这一行的格式为 4 p j
输入保证插入人名的字符串的长度和小于或等于 10 5,总的字符串的长度和小于或等于 10 6

输出描述:

对于每一次询问操作,在一行里面输出答案。
示例1

输入

20
1 a -10
1 abcba -9
1 abcbacd 5
4 a
2 a 9
3 aadaa
3 abcbacd
4 a
3 a
2 a 10
3 a
2 a -2
2 d -8
1 ab -2
2 ab -7
1 aadaa -3
4 a
3 abcba
4 a
4 c

输出

-14
0
14
13
-1
9
11
1
11
0

题目一看就知道是字典树,本来因为刚看了FZU的所以认为是字典树+树状数组维护,其实不用,只需要定时dfs更新下一层,这让我想起了并行计算。。。

代码:

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;

const int maxn = 1e6+7;

ll trie[maxn][27], tot = 0, sum[maxn], te[maxn], tg[maxn];
//trie表示字典树,tot表示树上的结点,sum[i]表示前缀为p[i]的声望和;te[i] 表示前缀出现的次数;
//tg[i]用来辅助第二个操作记录修改,使用的时候再刷新。。

void dfs(int x)
{
    for(int i = 0; i < 26; i++) if(trie[x][i]) tg[trie[x][i]] += tg[x];
    sum[x] += tg[x] * te[x];
    tg[x] = 0;
}


void insert(string s, int a)
{
    int len = s.length(), root = 0;
    for(int i = 0; i < len; i++) {
        int id = s[i] - 'a';
        if(!trie[root][id])
            trie[root][id] = ++tot;
        root = trie[root][id];
        dfs(root);
        sum[root] += a; te[root]++;
    }
}


void op2(string s, int a)
{
    int len = s.length(), root = 0;
    bool f = true;
    for(int i = 0; i < len; i++) {
        int id = s[i] - 'a';
        if(!trie[root][id])
        {
            f = false;
            break;
        }
        root = trie[root][id];
        dfs(root);
    }
    if(f) {
        tg[root] += a;
        int re = te[root];
        root = 0;
        for(int i = 0; i < len - 1; i++)
            root = trie[root][s[i] - 'a'], sum[root] += re*a;
    }
}


ll op4(string s)
{
    int len = s.length(), root = 0;
    bool f = true;
    for(int i = 0; i < len; i++) {
        int id = s[i] - 'a';
        if(!trie[root][id])
        {
            f = false;
            break;
        }
        root = trie[root][id];
        dfs(root);
    }
    if(f) return sum[root];
    else return 0;
}


ll op3(string s)
{
    int len = s.length(), root = 0;
    bool f = true;
    for(int i = 0; i < len; i++) {
        int id = s[i] - 'a';
        if(!trie[root][id])
        {
            f = false;
            break;
        }
        root = trie[root][id];
        dfs(root);
    }
    if(f)
    {
        ll ans = 0;
        for(int i = 0; i < 26; i++)
        if(trie[root][i]) {
            dfs(trie[root][i]);
            ans += sum[trie[root][i]];
        }
        return (sum[root] - ans);
    }
    else return 0;
}



int main()
{
    //freopen("into.txt","r",stdin);
    int n;
    string s;
    memset(trie, 0, sizeof(trie));
    memset(sum, 0, sizeof(sum));
    memset(vis, 0, sizeof(vis));
    memset(tg, 0, sizeof(tg));
    memset(te, 0, sizeof(te));
    scanf("%d", &n);
    while(n--)
    {
        int op, a;
        cin >> op;
        if(op == 1)
        {
            cin >> s >> a;
            insert(s, a);
        }
        else if(op == 2)
        {
            cin >> s >> a;
            op2(s, a);
        }
        else if(op == 4)
        {
            cin >> s;
            cout << op4(s) << endl;
        }
        else
        {
            cin >> s;
            cout << op3(s) << endl;
        }
    }
    //fclose(stdin);
    return 0;
}

我水平低,欢迎大佬指正。。。。。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值