Trie + 树上异或路径的应用

板子题

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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值