列表

详情


NC15706. 前缀查询

描述

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

输入描述

第一行为两个整数 0 ≤ N ≤ 105,表示接下来有 N 个操作;
接下来 N 行,每行输入一个操作,行首为一个整数 1 ≤ oi ≤ 4,表示这一行的操作的种类,
那么这一行的操作和格式为:
1. 插入人名,这一行的格式为 1 si ai,其中 |ai| ≤ 103
2. 前缀修改声望,这一行的格式为 2 pi di,其中 |di| ≤ 103
3. 查询名字的声望和,这一行的格式为 3 sj
4. 查询前缀的声望和,这一行的格式为 4 pj
输入保证插入人名的字符串的长度和小于或等于 105,总的字符串的长度和小于或等于 106

输出描述

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

示例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

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++(clang++ 11.0.1) 解法, 执行用时: 162ms, 内存消耗: 4360K, 提交时间: 2022-10-19 21:50:19

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll s[1000005];
char t[1000005];
int q,n,root,i,j,c[1000005][26],x[1000005],a[1000005];
void fix(int &r,char *t,int d)
{
	if(!r)r=++n;
	a[r]++;
	s[r]+=d;
	if(!*t)return;
	fix(c[r][*t-'a'],t+1,d-x[r]);
}
int fix1(int r,char *t,int d)
{
	if(!r)return 0;
	if(!*t)
	{
		x[r]+=d;
		s[r]+=(ll)a[r]*d;
		return a[r];
	}
    int f=fix1(c[r][*t-'a'],t+1,d);
	s[r]+=(ll)d*f;
    return f;
}
ll ask(int r,char *t,bool b,int f)
{
	if(!r)return 0;
	if(!*t)
	{
		ll ans=s[r]+(ll)f*a[r];
		if(b)for(int i=0;i<26;i++)ans-=ask(c[r][i],t,0,f+x[r]);
		return ans;
	}
	return ask(c[r][*t-'a'],t+1,b,f+x[r]);
}
int main()
{
	scanf("%d",&q);
	while(q--)
	{
		scanf("%d%s",&i,t);
		if(i==1)
		{
			scanf("%d",&i);
			fix(root,t,i);
		}
		else if(i==2)
		{
			scanf("%d",&i);
			fix1(root,t,i);
		}
		else cout<<ask(root,t,i&1,0)<<endl;
	}
	return 0;
}

上一题