trie树

trie树就是从当前节点往每个字母连一个指针,从而(O(26*n))或别的复杂度,反正有一个常数。表达能力不不行……

建树(插入)

void ins(char *s)
{
	int len=strlen(s),u=1;
	for(int i=0;i<=len-1;++i)
	{
		int v=s[i]-'a';
		if(!ch[u][v])ch[u][v]=++cnt;
		u=ch[u][v];
		++val[u];
	}
}

查询

int find(char *s)
{
	int u=1;
	int len=strlen(s);
	for(int i=0;i<=len-1;++i)
	if(ch[u][s[i]-'a'])u=ch[u][s[i]-'a'];
	else return 0;
	return val[u];
}

应该看完代码就能理解吧qwq

trie树最经典的应用是找与一个串有相同前缀的串有几个。

就是这题:HDU1251

#include<bits/stdc++.h>
using namespace std;
#define gc() getchar()
const int N=1000005;
char s[N][11],a[11];
int cnt=1,lens,n=1,ch[N][26],val[N*26];
bool f;
void ins(char *s)
{
	int len=strlen(s),u=1;
	for(int i=0;i<=len-1;++i)
	{
		int v=s[i]-'a';
		if(!ch[u][v])ch[u][v]=++cnt;
		u=ch[u][v];
		++val[u];
	}
}
int find(char *s)
{
	int u=1;
	int len=strlen(s);
	for(int i=0;i<=len-1;++i)
	if(ch[u][s[i]-'a'])u=ch[u][s[i]-'a'];
	else return 0;
	return val[u];
}
int main()
{
	char c;
	while(true)
	{
		c=gc();
		if(c==10)
		{
			if(f)break;
			else f=true,++n,lens=0;
		}
		else f=false,s[n][lens++]=c;
	}
	for(int i=1;i<=n;++i)ins(s[i]);
	while(scanf("%s",a)!=EOF)printf("%d
",find(a));
	return 0;
}

然后主要就是找异或最大值。这类问题就是把数二进制拆分反着插入,然后对于查询,每次选择与当前点权值(0/1)相反的节点继续往下,如果没有沿着相同的数走,一个贪心。

这题的意思是,给一个含有n个元素的集合,再给m个数,对于每个数求出与集合中数的异或最大值。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=100005;
ll T,n,m,a[N],cnt;
ll ch[N*32+5][2];
ll en[(N*32+5)<<1];
void ins(int x)
{
	ll u=1;
	ll i=(((ll)(1ll<<30ll))<<1ll);
	for(;i;i>>=1)
	{
		int c=(i&x)?1:0;
		if(!ch[u][c])ch[u][c]=++cnt;
		u=ch[u][c];
	}
	en[u]=x;
}
void clear()
{
	memset(ch,0,sizeof(ch));
	memset(en,0,sizeof(en));
	cnt=1;
}
ll find(int x)
{
	ll u=1;
	ll i=(((ll)(1ll<<30ll))<<1ll);
	for(;i;i>>=1)
	{
		ll c=(i&x)?0:1;
		if(ch[u][c])u=ch[u][c];
		else u=ch[u][!c];
	}
	return en[u];
}
signed main() {
	scanf("%lld",&T);
	for(int t=1;t<=T;++t)
	{
		scanf("%lld%lld",&n,&m);
		clear();
		for(int i=1;i<=n;++i)
			scanf("%lld",&a[i]),ins(a[i]);
		ll x;
		printf("Case #%d:
",t);
		while(m--)
		{
			scanf("%lld",&x);
			printf("%lld
",find(x));
		}
	}
	return 0;
}

BZOJ4260: Codechef REBXOR

很经典的做法。考虑用一个比较套路的DP,l[i]表示[1,i]的最大值,r[i]表示区间[i,n]的最大值,最后求max{l[i],r[i+1]}.

关于l[i],考虑求出异或前缀和(s_i=a_1igoplus a_2igoplus cdots igoplus a_i),最后 (a_iigoplus a_{i+1}igoplus cdots igoplus a_j=s_{i-1}igoplus s_j) ,然后针对 (s) 跑一遍上面那道题的做法就好了。

r[i]反着做一遍就好了。

#include<bits/stdc++.h>
using namespace std;
int max(int a,int b){return a>b?a:b;}
const int N=400005;
int n,a[N],ans,cnt,l[N],r[N],sum;
int ch[N<<5][2];
void ins(int x)
{
	int u=1;
	int i=1<<30;
	for(;i;i>>=1)
	{
		int c=(i&x)?1:0;
		if(!ch[u][c])ch[u][c]=++cnt;
		u=ch[u][c];
	}
}
int find(int x)
{
	int u=1,ans=0;
	int i=1<<30;
	for(;i;i>>=1)
	{
		int c=(i&x)?0:1;
		if(!ch[u][c])c=!c;
		u=ch[u][c];
		ans<<=1;ans|=c;
	}
	return ans^x;
}
void clear()
{
	cnt=1;sum=0;
	memset(ch,0,sizeof(ch));
}
signed main() {
	scanf("%d",&n);
	for(int i=1;i<=n;++i)scanf("%d",&a[i]);
	clear();
	for(int i=1;i<=n;++i)
	{
		sum^=a[i];
		ins(sum);
		l[i]=max(l[i-1],find(sum));
	}
	clear();
	for(int i=n;i>=1;--i)
	{
		sum^=a[i];
		ins(sum);
		r[i]=max(r[i+1],find(sum));
	}
	for(int i=1;i<n;++i)ans=max(ans,l[i]+r[i+1]);
	printf("%d
",ans);
	return 0;
}

关于异或,要充分利用前缀和,即 (xigoplus x=0) 的性质。

POJ3764 P4551 最长异或路径

这道题,可以把每个节点到根的异或和 ,(O(n)) 可以搞定,然后任取2个节点x,y,然后算一下异或和,会发现异或路径和就是2个节点向根的异或前缀和异或一下!

#include<cstdio>
#include<cstring>
using namespace std;
int max(int a,int b){return a>b?a:b;}
const int N=100005;
int n,a[N],num_edge,head[N],ans,cnt;
int ch[N<<5][2];
struct edge{
	int to,next,len;
}e[N<<1];
void add(int from,int to,int len)
{
	++num_edge;
	e[num_edge].len=len;
	e[num_edge].next=head[from];
	e[num_edge].to=to;
	head[from]=num_edge;
}
void dfs(int u,int fa)
{
	for(int i=head[u];i;i=e[i].next)
	{
		int v=e[i].to,w=e[i].len;
		if(v==fa)continue;
		a[v]=a[u]^w;
		dfs(v,u);
	}
}
void ins(int x)
{
	int u=1;
	int i=1<<30;
	for(;i;i>>=1)
	{
		int c=(i&x)?1:0;
		if(!ch[u][c])ch[u][c]=++cnt;
		u=ch[u][c];
	}
}
int find(int x)
{
	int u=1,ans=0;
	int i=1<<30;
	for(;i;i>>=1)
	{
		int c=(i&x)?0:1;
		if(!ch[u][c])c=!c;
		u=ch[u][c];
		ans<<=1;
		ans|=c;
	}
	return ans^x;
}
void clear()
{
	cnt=1;ans=0;
	num_edge=0;
	memset(ch,0,sizeof(ch));
	memset(head,0,sizeof(head));
	memset(e,0,sizeof(e));
}
signed main() {
	while(scanf("%d",&n)!=EOF)
	{
		clear();
		for(int i=1,u,v,w;i<n;++i)
		{
			scanf("%d%d%d",&u,&v,&w);
			++u,++v;
			add(u,v,w);
			add(v,u,w);
		}
		dfs(1,0);
		for(int i=1;i<=n;++i)ins(a[i]);
		for(int i=1;i<=n;++i)ans=max(ans,find(a[i]));
		printf("%d
",ans);
	}
	return 0;
}
路漫漫其修远兮,吾将上下而求索
原文地址:https://www.cnblogs.com/zzctommy/p/12359316.html