模板
const int maxn =2e6+5;
int tree[maxn][30];
bool flagg[maxn];
int tot;
void insert_(char *str)
{
int len=strlen(str);
int root=0;
for(int i=0;i<len;i++)
{
int id=str[i]-'0';
if(!tree[root][id]) tree[root][id]=++tot;
root=tree[root][id];
}
flagg[root]=true;
}
bool find_(char *str)
{
int len=strlen(str);
int root=0;
for(int i=0;i<len;i++)
{
int id=str[i]-'0';
if(!tree[root][id]) return false;
root=tree[root][id];
}
return true;
}
void init()
{
for(int i=0;i<=tot;i++)
{
flagg[i]=false;
for(int j=0;j<26;j++)
tree[i][j]=0;
}
tot=0;
}
例题
POJ-2513 Colored Sticks
将一种颜色看成一块陆地,一根木棍看作一座桥(两块陆地之间可以有多座桥),求是否存在一条通路使得经过每座桥一次且仅一次。因此这道题变成欧拉图的判定,以一种颜色为陆地,每根木棍看作一座桥,使得两块陆地的度数分别加1。
在判定颜色时,由于木棍数量较多,若使用STL中的map会超时,因此应改用Trie树。
判定欧拉图需使用并查集检查图是否连通。因此这道题需用到并查集+Trie树
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
#include<vector>
#include<map>
#include<set>
#define INF 10000000000000
using namespace std;
//字典树:
const int maxn =2e6+5;
int tree[maxn][30];
int flagg[maxn];
int tot;
int color=0;
int insert_(char *str)
{
int len=strlen(str);
int root=0;
for(int i=0;i<len;i++)
{
int id=str[i]-'0';
if(!tree[root][id]) tree[root][id]=++tot;
root=tree[root][id];
}
if(!flagg[root]){
color++;
flagg[root]=color;
}
return flagg[root];
}
//并查集模板:
const int mn=5e5+10;
int par[mn],Rank[mn];
void init(){
for(int i=0;i<=mn;i++)
{
par[i]=i;
}
}
int find(int x)
{
return x==par[x]?x:par[x]=find(par[x]);
}
void unite(int x,int y)
{
x=find(x);y=find(y);
if(Rank[x]<Rank[y])
{
par[x]=y;
}else{
par[y]=x;
if(Rank[x]==Rank[y]) Rank[x]++;
}
}
char s1[12],s2[12];
int cnt[mn];
int main()
{
init();
while(~scanf("%s%s",s1,s2))
{
int color1=insert_(s1),color2=insert_(s2);
unite(color1,color2);
cnt[color1]++;
cnt[color2]++;
}
int Cnt=0;
bool res=true;
int cmp=find(1);
for(int i=1;i<=color;i++)
{
if(find(i)!=cmp){
res=false;
break;
}
if(cnt[i]%2)Cnt++;
if(Cnt>2)break;
}
if(Cnt!=0&&Cnt!=2)res=false;
if(res){
printf("Possible\n");
}else{
printf("Impossible\n");
}
return 0;
}
POJ-1451 T9
注意权值是加在原来的权值上,而不是覆盖原来的权值。如果是新权值覆盖旧权值,则不需要用到字典树,只需要建立一个前缀与权值的映射即可。如果是权值加到原来的权值上,则需要使用字典树来记录各个节点的权值。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
#include<vector>
#include<map>
#include<set>
#define INF 10000000000000
using namespace std;
const int mn=2e5+10;
const double esp=1e-9;
const int mod=1e9+7;
map<string,string> f;
map<string,int>cmp;
map<char,char>trans;
const int maxn =2e6+5;
int tree[maxn][30];
int sum[maxn];
bool flagg[maxn];
int tot;
void insert_(char *str,int P)
{
int len=strlen(str);
int root=0;
string num="";
string tmp="";
for(int i=0;i<len;i++)
{
num+=trans[str[i]];
tmp+=str[i];
int id=str[i]-'a';
if(!tree[root][id]) tree[root][id]=++tot;
root=tree[root][id];
sum[root]+=P;
if(cmp[num]<sum[root]){
cmp[num]=sum[root];
f[num]=tmp;
}else if(cmp[num]==sum[root]){
if(f[num]>tmp) f[num]=tmp;
}
}
flagg[root]=true;
}
void init()
{
f.clear();
cmp.clear();
for(int i=0;i<=tot;i++)
{
flagg[i]=false;
sum[i]=0;
for(int j=0;j<26;j++)
{
tree[i][j]=0;
}
}
tot=0;
}
int main()
{
trans['a']=trans['b']=trans['c']='2';//建立字母与数字的映射
trans['d']=trans['e']=trans['f']='3';
trans['g']=trans['h']=trans['i']='4';
trans['j']=trans['k']=trans['l']='5';
trans['m']=trans['n']=trans['o']='6';
trans['p']=trans['q']=trans['r']=trans['s']='7';
trans['t']=trans['u']=trans['v']='8';
trans['w']=trans['x']=trans['y']=trans['z']='9';
int t;
cin>>t;
for(int Case=1;Case<=t;Case++)
{
init();
int n;
scanf("%d",&n);
char str[105];
int P;
while(n--)
{
scanf("%s",str);
scanf("%d",&P);
insert_(str,P);
}
int m;
scanf("%d",&m);
printf("Scenario #%d:\n",Case);
while(m--)
{
char s[105];
scanf("%s",s);
string S="";
for(int i=0;i<strlen(s)-1;i++)
{
S+=s[i];
if(cmp[S]==0){
printf("MANUALLY\n");
}else{
printf("%s\n",f[S].c_str());
}
}
printf("\n");
}
printf("\n");
}
return 0;
}
HDU 4825
给一个数,取集合中的一个数与之求异或,问取哪个数异或后最大。
字典树与XOR问题的结合。
如图,建立一个二叉树,将一个数转化为二进制,从高位到低位将数位放于树中,树的一个叶子节点代表一个数。高度度为n树最多能表示 2n+1-1 个正整数。
按题目要求,建树则将十进制的数转化为二进制,从高位到低位将数字存进树中。查询的优先查找与查询数数位不同的节点,查到叶子节点即为所求数字。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
typedef long long LL;
using namespace std;
const int mn=1e5+10;
int tree[mn*35][2];
int flagg[mn*35];
int tot;
void insert_(int x)
{
int root=0;
for(int i=31;i>=0;i--)
{
int id=(x>>i)&1;//和1做与运算后数只剩第一位(例:100 & 001 = 000 ; 101 & 001 = 001)
if(!tree[root][id]) tree[root][id]=++tot;
root=tree[root][id];
}
flagg[root]=x;
}
int find_(int x)
{
int root=0;
for(int i=31;i>=0;i--)
{
int id=(x>>i)&1;
if(tree[root][id^1]) root=tree[root][id^1];
else root=tree[root][id];
}
return flagg[root];
}
void init()//最后清空,节省时间
{
for(int i=0;i<=tot;i++)
{
flagg[i]=0;
for(int j=0;j<2;j++)
tree[i][j]=0;
}
tot=0;//RE有可能是这里的问题
}
int main()
{
int t;
cin>>t;
for(int C=1;C<=t;C++)
{
init();
int n,m;
scanf("%d%d",&n,&m);
while(n--)
{
int a;
scanf("%d",&a);
insert_(a);
}
printf("Case #%d:\n",C);
while(m--)
{
int a;
scanf("%d",&a);
int res=find_(a);
printf("%d\n",res);
}
}
return 0;
}
Codeforces 665E
求区间异或大于k的区间数。
对区间做前缀异或s[ i ]=s[1] ^ s[2] ^ s[3] ^ … ^ s[ i ],那么区间( L,R ]的异或和为s[ R ] ^ s[ L ]
参考题解
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
typedef long long LL;
using namespace std;
const int mn=1e6+10;
int tree[mn*32][2];
LL flagg[mn*32];
int tot;
int n,k;
void insert_(int x)
{
int root=0;
for(int i=31;i>=0;i--)
{
int id=(x>>i)&1;//和1做与运算后数只剩第一位(例:100 & 001 = 000 ; 101 & 001 = 001)
if(!tree[root][id]) tree[root][id]=++tot;
root=tree[root][id];
flagg[root]++;
}
}
LL find_(int x)
{
LL sum=0;
int root=0;
for(int i=31;i>=0;i--)
{
int id=(x>>i)&1,K=(k>>i)&1;
if(!K){
sum+=flagg[tree[root][id^1]];
root=tree[root][id];
}else{
root=tree[root][id^1];
}
if(!root)return sum;
}
return sum+flagg[root];
}
int main()
{
cin>>n>>k;
insert_(0);
LL tmp=0,ans=0;
while(n--)
{
int a;
cin>>a;
tmp^=a;
ans+=find_(tmp);
insert_(tmp);
}
cout<<ans;
return 0;
}