前缀查询(维护字典树前缀和)
题目链接:
https://ac.nowcoder.com/acm/problem/15706
题目大意:
现在需要您来帮忙维护这个名册,支持下列 4 种操作:
1. 插入新人名 si,声望为 ai
2. 给定名字前缀 pi 的所有人的声望值变化 di
3. 查询名字为 sj 村民们的声望值的和(因为会有重名的)
4. 查询名字前缀为 pj 的声望值的和
2. 给定名字前缀 pi 的所有人的声望值变化 di
3. 查询名字为 sj 村民们的声望值的和(因为会有重名的)
4. 查询名字前缀为 pj 的声望值的和
具体思路:
一开始是这样想的,在线查询转成离线查。把树建完之后,对整个字典树跑一个dfs序。对于操作一,就是简单的字典树赋权值。对于操作二,就是更新以对当前pi结尾的子树整体赋值di*这段区间的val个数。对于操作三,就是单点查询。对于操作四,就是查询以pj节点的子树的权值和。打出来发现了一个致命的问题,因为你已经将整个字典树,在你维护区间和的时候,
如果这一段本来应该没有值,但是你的字典树中是仍存在的,也就是这一段区间的值本来应该不应该更新还是更新了。把代码贴上吧,说不定啥时候就能改过来了。
错误代码:
1 #pragma comment(linker,"/STACK:1024000000,1024000000")
2 #include<bits/stdc++.h>
3 using namespace std;
4 # define ll long long
5 # define inf 0x3f3f3f3f
6 # define lson l,mid,rt<<1
7 # define rson mid+1,r,rt<<1|1
8 const int maxn = 1e5+200;
9 int ch[maxn][30];
10 struct node
11 {
12 int type,cost;
13 string str;
14 node() {}
15 node(int xx,int yy,string zz){
16 type=xx,cost=yy,str=zz;
17 }
18 } q[maxn];
19 int tot=0;
20 void add_trie_build(string str)
21 {
22 int p=0;
23 int len=str.size();
24 for(int i=0; i<len; i++)
25 {
26 int u=str[i]-'a';
27 if(!ch[p][u])
28 ch[p][u]=++tot;
29 p=ch[p][u];
30 }
31 }
32 int L[maxn],R[maxn];
33 int dfs_ord;
34 void dfs(int p)
35 {
36 L[p]=++dfs_ord;
37 for(int i=0; i<26; i++)
38 {
39 if(!ch[p][i])
40 continue;
41 dfs(ch[p][i]);
42 }
43 R[p]=dfs_ord;
44 }
45 int tree[maxn<<2],lazy[maxn<<2];
46 void down(int rt)
47 {
48 tree[rt<<1]+=lazy[rt];
49 tree[rt<<1|1]+=lazy[rt];
50 lazy[rt<<1]+=lazy[rt];
51 lazy[rt<<1|1]+=lazy[rt];
52 lazy[rt]=0;
53 }
54 void up(int rt)
55 {
56 tree[rt]=tree[rt<<1]+tree[rt<<1|1];
57 }
58 void update(int l,int r,int rt,int L,int R,int cost)
59 {
60 if(L<=l&&R>=r)
61 {
62 tree[rt]+=cost;
63 lazy[rt]+=cost;
64 return ;
65 }
66 int mid=l+r>>1;
67 down(rt);
68 if(L<=mid)
69 update(lson,L,R,cost);
70 if(R>mid)
71 update(rson,L,R,cost);
72 up(rt);
73 }
74 int ask(int l,int r,int rt,int L,int R)
75 {
76 if(L<=l&&R>=r)
77 {
78 return tree[rt];
79 }
80 int mid=l+r>>1;
81 int sum=0;
82 down(rt);
83 if(L<=mid)
84 sum+=ask(lson,L,R);
85 if(R>mid)
86 sum+=ask(rson,L,R);
87 up(rt);
88 return sum;
89 }
90 int val[maxn];
91 int get_id(string str,int type)
92 {
93 int p=0;
94 int len=str.size();
95 for(int i=0; i<len; i++)
96 {
97 int u=str[i]-'a';
98 p=ch[p][u];
99 if(type)val[p]++;
100 }
101 return p;
102 }
103 void add_trie_update_type1(string str,int cost)
104 {
105 int p=get_id(str,1);
106 update(1,dfs_ord,1,L[p],L[p],cost);
107 }
108 void add_trie_update_type2(string str,int cost)
109 {
110 int p=get_id(str,0);
111 update(1,dfs_ord,1,L[p],R[p],cost*val[p]);
112 }
113 int solve(string str,int type)
114 {
115 int id=get_id(str,0);
116 if(type==3)
117 {
118 return (val[id]>0?1:0)*ask(1,dfs_ord,1,L[id],L[id]);
119 }
120 else if(type==4)
121 {
122 return (val[id]>0?1:0)*ask(1,dfs_ord,1,L[id],R[id]);
123 }
124 }
125 string str;
126 vector<int>ans;
127 int main()
128 {
129 ios::sync_with_stdio(false);
130 int n;
131 cin>>n;
132 int type,cost;
133 for(int i=1; i<=n; i++)
134 {
135 cin>>type;
136 if(type==1)
137 {
138 cin>>str>>cost;
139 add_trie_build(str);
140 q[i]=node(type,cost,str);
141 }
142 else if(type==2)
143 {
144 cin>>str>>cost;
145 q[i]=node(type,cost,str);
146 }
147 else if(type==3)
148 {
149 cin>>str;
150 q[i]=node(type,0,str);
151 }
152 else if(type==4)
153 {
154 cin>>str;
155 q[i]=node(type,0,str);
156 }
157 }
158 dfs(0);
159 for(int i=1; i<=n; i++)
160 {
161 if(q[i].type==1)
162 {
163 add_trie_update_type1(q[i].str,q[i].cost);
164 }
165 else if(q[i].type==2)
166 {
167 add_trie_update_type2(q[i].str,q[i].cost);
168 }
169 else if(q[i].type==3)
170 {
171 printf("%d\n",solve(q[i].str,3));
172 }
173 else if(q[i].type==4)
174 {
175 printf("%d\n",solve(q[i].str,4));
176 }
177 }
178 return 0;
179 }
然后阿刘给我讲了他的做法。
维护这个字典树的前缀和,w[i]代表在字典树上,编号为i的节点所代表的前缀,包含这个前缀的字符串的总的权值为多少。但是需要考虑到一个问题,在你查询abb的时候,假设之前有一个a已经更新了,然后让你查询abb这个串的权值,这个时候你是需要在树上往后更新的,所以这里需要打一个lazy标记,每次动态更新就好了。
1 #pragma comment(linker,"/STACK:1024000000,1024000000")
2 #include<bits/stdc++.h>
3 using namespace std;
4 # define ll long long
5 # define inf 0x3f3f3f3f
6 # define lson l,mid,rt<<1
7 # define rson mid+1,r,rt<<1|1
8 # define int ll
9 const int maxn = 2e5+200;
10 int ch[maxn][30];
11 int val[maxn],w[maxn];
12 int lazy[maxn];
13 int tot;
14 void down(int p)
15 {
16 for(int i=0; i<26; i++)
17 {
18 int u=ch[p][i];
19 if(!u)
20 continue;
21 w[u]+=lazy[p]*val[u];
22 lazy[u]+=lazy[p];
23 }
24 lazy[p]=0;
25 }
26 void add_type1(string str,int cost)
27 {
28 int p=0;
29 int len=str.size();
30 for(int i=0; i<len; i++)
31 {
32 int u=str[i]-'a';
33 if(!ch[p][u])
34 ch[p][u]=++tot;
35 p=ch[p][u];
36 val[p]++;
37 w[p]+=cost;
38 down(p);
39 }
40 }
41 void add_type2(string str,int cost)
42 {
43 queue<int>q;
44 while(!q.empty())
45 q.pop();
46 int len=str.size();
47 int p=0;
48 for(int i=0; i<len; i++)
49 {
50 int u=str[i]-'a';
51 if(!ch[p][u])
52 ch[p][u]=++tot;
53 p=ch[p][u];
54 down(p);
55 q.push(p);
56 }
57 lazy[p]+=cost;
58 while(!q.empty())
59 {
60 int top=q.front();
61 w[top]+=cost*val[p];
62 q.pop();
63 }
64 }
65 int ask_type3(string str)
66 {
67 int len=str.size();
68 int p=0;
69 for(int i=0; i<len; i++)
70 {
71 int u=str[i]-'a';
72 if(!ch[p][u])
73 ch[p][u]=++tot;
74 p=ch[p][u];
75 down(p);
76 }
77 int sum=w[p];
78 for(int i=0; i<26; i++)
79 {
80 int u=ch[p][i];
81 if(!u)
82 continue;
83 sum-=w[ch[p][i]];
84 }
85 return sum;
86 }
87 int ask_type4(string str)
88 {
89 int len=str.size();
90 int p=0;
91 for(int i=0; i<len; i++)
92 {
93 int u=str[i]-'a';
94 if(!ch[p][u])
95 ch[p][u]=++tot;
96 p=ch[p][u];
97 down(p);
98 }
99 return w[p];
100 }
101 string str;
102 signed main()
103 {
104 int n,type,cost;
105 cin>>n;
106 for(int i=1; i<=n; i++)
107 {
108 cin>>type;
109 if(type==1)
110 {
111 cin>>str>>cost;
112 add_type1(str,cost);
113 }
114 else if(type==2)
115 {
116 cin>>str>>cost;
117 add_type2(str,cost);
118 }
119 else if(type==3)
120 {
121 cin>>str;
122 cout<<ask_type3(str)<<endl;
123 }
124 else if(type==4)
125 {
126 cin>>str;
127 cout<<ask_type4(str)<<endl;
128 }
129 }
130 return 0;
131 }