板子题
CH1601 前缀统计
给定N个字符串S1,S2…SN,接下来进行M次询问,每次询问给定一个字符串T,求S1~SN中有多少个字符串是T的前缀。
输入字符串的总长度不超过106,仅包含小写字母。
输入格式
第一行输入两个整数N,M。
接下来N行每行输入一个字符串Si。
接下来M行每行一个字符串T用以询问。
输出格式
对于每个询问,输出一个整数表示答案。
每个答案占一行。
输入样例:
3 2
ab
bc
abc
abc
efg
输出样例:
2
0
#include <string>
#include <iostream>
using namespace std;
const int SIZE=1e6;
int n,m,trie[26][SIZE],tot = 1,endn[SIZE];
string s;
void insert(string str){
int len = str.size(), p = 1;
for (int k=0;k<len;k++){
int ch = str[k] -'a';
if (trie[ch][p] == 0) trie[ch][p] = ++tot;
p = trie[ch][p];
}
endn[p] ++;
}
int search(string str){
int len = str.size(), p=1;
int ans = 0;
for (int k=0;k<len;k++){
p = trie[str[k]-'a'][p];
if (p == 0) return ans;
ans += endn[p];
//if (p == 0 ) return false;
}
return ans;
}
int main(){
cin >> n >> m;
for (int i=1;i<=n;i++){
cin >> s;
insert(s);
}
for (int i=1;i<=m;i++){
cin >> s;
cout << search(s) << endl;
}
}
用 Trie 处理最大异或问题
[CH1602 The XOR Largest Pair]
(https://www.acwing.com/problem/content/description/145/)
在给定的N个整数A1,A2……AN中选出两个进行xor(异或)运算,得到的结果最大是多少?
输入格式
第一行输入一个整数N。
第二行输入N个整数A1~AN。
输出格式
输出一个整数表示答案。
数据范围
1≤N≤105,
0≤Ai<231
输入样例:
3
1 2 3
输出样例:
3
把一个数拆分成32位数(其实这题拆分成31就够了),然后跑一遍Trie,尽量选择和当前数的数位不同的,从高到低保证贪心的可行性,如果无法选到就选另外一条路的。
#include <string>
#include <iostream>
using namespace std;
const int SIZE=1e5+100;
int n,m,trie[2][SIZE*32],tot = 1,endn[SIZE];
string s;
void insert(int x){
int p = 1;
for (int k=31;k>=0;k--){
int ch = x >> k&1;
if (trie[ch][p] == 0) trie[ch][p] = ++tot;
p = trie[ch][p];
}
}
int search(int x){
int p = 1,ans = 0;
for (int k=31;k>=0;k--){
int ch = x >> k &1;
if (trie[ch^1][p]) {
p = trie[ch^1][p];
ans |= (1<<k);
}
else
p = trie[ch][p];
}
return ans;
}
int main(){
cin >> n;
int x,ans=0;
for (int i=1;i<=n;i++){
cin >> x;
insert(x);
ans = max(ans,search(x));
}
cout << ans << endl;
}
POJ3764 The XOR Longest Path
传送门
给定一个树,树上的边都具有权值。
树中一条路径的异或长度被定义为路径上所有边的权值的异或和:
formula.png
⊕ 为异或符号。
给定上述的具有n个节点的树,你能找到异或长度最大的路径吗?
输入格式
第一行包含整数n,表示树的节点数目。
接下来n-1行,每行包括三个整数u,v,w,表示节点u和节点v之间有一条边权重为w。
输出格式
输出一个整数,表示异或长度最大的路径的最大异或和。
数据范围
1≤n≤100000,
0≤u,v<n,
0≤w<231
输入样例:
4
0 1 3
1 2 4
1 3 6
输出样例:
7
样例解释
样例中最长异或值路径应为0->1->2,值为7 (=3 ⊕ 4)
乍一眼看,树上路径异或和,树剖?线性基?其实还是 Trie ,用到树上异或和的可减性质,可以预处理类似前缀和的根节点到每个子节点的路径异或和,然后就转化为了只要在n个树中差分选择一个最大的差分值就ok。n<=1e5,n方配对肯定不行,所以又转化到了上题的Trie树求解方式。
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn=2*1e5+100;
LL v[maxn],ver[maxn],edge[maxn],nextr[maxn],head[maxn],sum[maxn],trie[2][maxn*16];
int n,tot=1;
inline LL read()//快速读入
{
char ch=getchar();
int p=1,q=0;
for(; ch!='-' && ch<'0' || ch>'9'; ch=getchar());
if (ch=='-')
p=-p;
for(; ch>='0' && ch<='9'; q=q*10+ch-'0',ch=getchar());
return p*q;
}
inline void add(int x,int y,int z){
ver[++tot] = y,edge[tot] = z;
nextr[tot] = head[x],head[x] = tot;
}
inline void dfs(int x){
v[x] = 1;
for (int i=head[x];i;i=nextr[i]){
if (!v[ver[i]]){
sum[ver[i]] = sum[x] ^ edge[i];
dfs(ver[i]);
}
}
}
inline void insert(int x){
int p = 1;
for (int k=31;k>=0;k--){
int ch = x >>k &1;
if (trie[ch][p] == 0) trie[ch][p] =++tot;
p = trie[ch][p];
}
}
inline LL search(int x){
LL p = 1,ans = 0;
for (int k=31;k>=0;k--){
int ch = x>>k&1;
if (trie[ch^1][p]) {
p = trie[ch^1][p];
ans|=(1<<k);
}else{
p = trie[ch][p];
}
}
return ans;
}
int main(){
cin >> n;
memset(v,0,sizeof(v));
for (int i=1;i<n;i++){
LL x,y,z;
x = read(),y = read(),z = read();
x++,y++;
add(x,y,z);
add(y,x,z);
}
memset(sum,0,sizeof(sum));
dfs(1);
tot =1;
LL ans = 0;
for (int i=1;i<=n;i++){
insert(sum[i]);
ans = max(ans,search(sum[i]));
}
cout << ans ;
}
一直从报错Segmentation Fault,调了好久才把这个题调出来,原来是trie数组开小了,在这里记得要开 32* SIZE。