前缀查询(维护字典树前缀和)

题目链接:

https://ac.nowcoder.com/acm/problem/15706

题目大意:

现在需要您来帮忙维护这个名册,支持下列 4 种操作:

1. 插入新人名 si,声望为 ai
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
",solve(q[i].str,3));
172         }
173         else if(q[i].type==4)
174         {
175             printf("%d
",solve(q[i].str,4));
176         }
177     }
178     return 0;
179 }
View Code
然后阿刘给我讲了他的做法。
维护这个字典树的前缀和,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 }
原文地址:https://www.cnblogs.com/letlifestop/p/10953601.html