字典樹Trie
結構:
trie的結構就是一個類似於字首匹配的結構,我們用 \(\delta (u,c)\) 表示節點 \(u\) 指向的下一個儲存的資訊為 \(c\) 的節點編號。
應用:
簡單查詢字串:
P2580 於是他錯誤的點名開始了
建立一顆由小寫字母為字符集組成的Trie樹,每次查詢都從根節點開始,一個一個向下查詢,如果在某個節點發現沒有下個節點的字母與當前要詢問的名字的字母相同時,則為 WRONG
,如果存在該名字,我們對這個末尾節點開一個計數的數,如果這個數 \(>0\),那麼就輸出 REPEAT
,否則輸出 OK
。
code:
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 1e4 + 7;
int cnt = 1,trie[MAXN * 50][27];
int has[MAXN * 50];
int n,m;
string s;
void insert(string s){
int len = s.length(),p = 1;
for(int i = 1;i <= len;i++){
if(trie[p][s[i - 1] - 'a']) p = trie[p][s[i - 1] - 'a'];
else cnt++,trie[p][s[i - 1] - 'a'] = cnt,p = cnt;
}
has[p] = 1;
}
int find(string s){
int len = s.length(),p = 1;
for(int i = 1;i <= len;i++){
if(trie[p][s[i - 1] - 'a']) p = trie[p][s[i - 1] - 'a'];
else return 2;
}
if(has[p] == 1) return has[p]++,0;
else return has[p]++,1;
}
int main(){
scanf("%d", &n);
for(int i = 1;i <= n;i++) cin >> s,insert(s);
scanf("%d", &m);
for(int i = 1;i <= m;i++){
cin >> s;
if(find(s) == 0) printf("OK\n");
else if(find(s) == 1) printf("REPEAT\n");
else printf("WRONG\n");
}
return 0;
}
程式碼好實現。
最長異或路徑
[P4551 最長異或路徑](P4551 最長異或路徑)
我們已知異或具有字首和的性質,那麼我們設根節點為 \(1\),而 \(val(x,y)\) 表示 \(x \to y\) 的路徑異或值,則有:
\[val(x,y) = val(1,x) \oplus val(1,y)
\]
即求 \(\max_{x,y \in [1,n]} val(x,y)\)。
我們預處理出 \(dis_x\) 陣列表示 \(x\) 點到根節點( \(1\) 號節點)的路徑異或和。
建立一顆 01 Trie
樹,把 \(dis\) 陣列的每個值從高位到低位插入Trie,深度大約為30左右。
然後列舉 \(x\) 節點,從高位到低位遍歷Trie樹,存在一個當前遍歷到的節點的子節點滿足與 \(dis_x\) 當前遍歷到的二進位制位上的 \(0/1\) 值相反,那麼就遍歷Trie樹的這一個子節點。
這實際上是一種貪心的思想,因為從高位到低位,我們如果二進位制位上的值相反
,那麼就選擇這一位,因為高位上的值價值大於低位。所以我們建樹的過程也是從高位往低位建樹。
那麼複雜度就是 \(O(nlogV)\),可過。
code:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXN = 1e5 + 7;
const int LOG = 30;
struct EDGE{ int x,val; };
int n, x, y, v;
vector<EDGE> tree[MAXN];
int val[MAXN];
int cnt = 1, trie[MAXN * LOG][2], ans;
void insert(int val){
int p = 1;
for(int i = 30;i >= 0;i--){
int op = ((val >> i) & 1);
if(trie[p][op]) p = trie[p][op];
else cnt++,trie[p][op] = cnt,p = cnt;
}
}
void dfs(int pos,int fa){
for(auto to : tree[pos]) if(to.x != fa) val[to.x] = to.val ^ val[pos],insert(val[to.x]),dfs(to.x,pos);
}
void cal(int pos){
int res = 0,p = 1;
for(int i = 30;i >= 0;i--){
int op = (val[pos] >> i) & 1;
if(trie[p][op ^ 1]) p = trie[p][op ^ 1], res = res | (1 << i);
else p = trie[p][op];
}
ans = max(ans,res);
}
void dfs1(int pos,int fa){
cal(pos);
for(auto to : tree[pos]) if(to.x != fa) dfs1(to.x,pos);
}
int main(){
scanf("%d", &n);
for(int i = 1;i <= n - 1;i++) {
scanf("%d%d%d", &x, &y, &v);
tree[x].push_back({y,v}),tree[y].push_back({x,v});
}
dfs(1,0),dfs1(1,0);
printf("%d", ans);
return 0;
}