1. 程式人生 > 實用技巧 >【Trie】補退選(洛谷P5335)

【Trie】補退選(洛谷P5335)

題目描述

X 是一名老師,每年他都要教授許多學生基礎的C++ 知識。每個學生在每學
期的開學前都需要選課,每次選課一共分為三個階段:預選,正選,補退選;其
中「補退選」階段最忙碌。
在補退選階段,學生即可以選課,也可以退課。對於X 老師來說,在補退選階
段可能發生以下兩種事件:
• 一個姓名為S 的學生選了他的課(姓名S 將出現在已選課學生名單中);
• 一個姓名為S 的學生退了他的課(姓名S 將從已選課學生名單中移除)。
同時,X 老師對於有哪些學生選了他的課非常關心,所以他會不定時的查詢已
選課學生名單,每次查詢為:
• 最早在哪個事件之後,姓名以S 為字首的學生數量超過了v。
X 老師看你骨骼清奇,所以想用這個問題考考你,你當然不會畏懼,所以勇敢
的接下了這個任務。
注意1:學生的姓名可能相同,如果有p 個姓名相同的學生都選了X 老師的課,
則他們的姓名將出現在X 老師的名單上p 次。
注意2:只有已經選了課的學生才會退課,如果姓名為S 的學生退課,則在他
退課之前X 老師的名單上一定有姓名S。
注意3:選課,退課和查詢都被定義為「事件」;「事件」的編號從1 開始。

輸入

第一行包含一個正整數n,表示一共發生了n 個事件。
接下來n 行,每行描述一個事件;每行第一個正整數k 表示事件型別:
• 如果k = 1,表示選課事件,接下來一個字串S,表示一個姓名為S 的
學生選了X 老師的課;
• 如果k = 2,表示退課事件,接下來一個字串S,表示一個姓名為S 的
學生退了X 老師的課;
• 如果k = 3,表示查詢事件,接下來一個字串S 以及三個非負整數a, b, c,
表示X 老師想知道最早在第幾個事件之後,姓名以S 為字首的學生數量
超過了(a×|ans|+b) mod c,|ans| 表示上次查詢事件的答案的絕對值,如
果當前是第一次查詢,則|ans| = 0;如果任何時刻都沒有超過該值,則答
案為−1。
注意:輸入中的所有字串均只包含前10 個小寫字母。

輸出

對於每個查詢事件,輸出一行表示該查詢答案。

資料範圍限制

對於10% 的資料,1 ≤ n ≤ 100。
對於20% 的資料,1 ≤ n ≤ 4000。
對於50% 的資料,1 ≤ n ≤ 30000。
對於100% 的資料,1 ≤ n ≤ 100000,1 ≤ |S| ≤ 60。

Soultion:

看到字首查詢就很容易想到Trie樹,本題難點在於如何實現查詢最早出現位置,可持久化空間消耗太大(而且咱也不會打),離線查詢也不現實,所以考場上打完trie就蒙圈了。

正解是利用vector記錄trie樹上每個端點第一次到達某個數值時操作的次數,查詢的時候只要查到最後一個字母時對應數值第一次達到的操作次數,直接輸出即可,如果不存在輸出-1。強 thm 強

程式碼有點菜,最久的點跑了800ms。

Code:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int N,tot;
 4 int Tree[555555][10],son[555555][10];
 5 int Action[100500],Ans;
 6 char Str[66];
 7 vector<int> Mark[555555][10];
 8 void insert(int Action){
 9     int Len=strlen(Str);
10     int p=1;
11     for(int i=0;i<Len;i++){
12         int Num=Str[i]-'a';
13         Tree[p][Num]++;
14         if(Mark[p][Num].size()<Tree[p][Num]) Mark[p][Num].push_back(Action);
15         if(i==Len-1) continue;
16         if(!son[p][Num]) son[p][Num]=++tot;
17         p=son[p][Num];
18     }
19 }
20 void Delete(){
21     int Len=strlen(Str);
22     int p=1;
23     for(int i=0;i<Len;i++){
24         int Num=Str[i]-'a';
25         Tree[p][Num]--;
26         if(i==Len-1) continue;
27         p=son[p][Num];
28     }
29 }
30 void Search(int goal){
31     int Len=strlen(Str);
32     int p=1;
33     for(int i=0;i<Len;i++){
34         int Num=Str[i]-'a';
35         if(i==Len-1) continue;
36         p=son[p][Num];
37     }
38     int Num2=Str[Len-1]-'a';
39     if(Mark[p][Num2].size()>goal)
40         Ans=Mark[p][Num2][goal];
41     else Ans=-1;
42 }
43 int main()
44 {
45 //    freopen("selection.in","r",stdin);
46 //    freopen("selection.out","w",stdout);
47     int m=0;
48     tot=1;
49     scanf("%d",&N);
50     for(int __=1;__<=N;__++){
51         int Key;
52         scanf("%d",&Key);
53         if(Key==1){
54             scanf("%s",Str);
55             insert(__);
56         }
57         if(Key==2){
58             scanf("%s",Str);
59             Delete();
60         }
61         if(Key==3) {
62             scanf("%s",Str);
63             long long a,b,c;
64             cin>>a>>b>>c;
65             Search((a*Ans+b)%c);
66             cout<<Ans<<endl;
67             if(Ans==-1) Ans=1;
68         }
69     }
70     return 0;
71 }