Trie专题题解报告
vj题
POJ2001-Shortest Prefixes
考点:trie统计前缀
题目大意
对单词进行缩写,但每个单词缩写不能有重复;
解题思路
本质上就是统计一下每个单词的前缀有多少个单词在用,当只有一个单词再用的时候,那就是答案;
我们写出我们的搜索函数,返回在字符串中的位置,再取出相应的子串,然后输出;
#include<iostream>
#include<algorithm>
#include<string>
#include<map>
#include<cstring>
#include<climits>
#include<cfloat>
using namespace std;
const int N=2e4+100;
struct node{
int Next[26];
int sum;
}trie[N];int id;
string str[N];int size__;
void ins(string str)
{
int p=0;
for(int i=0;i<str.size();i++){
int ch=str[i]-'a';
if (!trie[p].Next[ch])
trie[p].Next[ch]=++id;
p=trie[p].Next[ch];
trie[p].sum++;
}
}
int Find(string str)
{
int p=0;
int len=str.size();
for(int i=0;i<len;i++){
int ch=str[i]-'a';
p=trie[p].Next[ch];
if( trie[p].sum==1 ){
return i+1;
}
}
return len;
}
signed main(void)
{
cin.tie(0)->sync_with_stdio(0);
cout.tie(0)->sync_with_stdio(0);
for(size__=1; getline(cin,str[size__]) ;size__++){
ins(str[size__]);
}
for(int i=1;i<=size__;i++){
cout<<str[i]<<' '<<str[i].substr(0,Find( str[i] ) )<<'\n';
}
return 0;
}
POJ3630-Phone List
考点:trie统计前缀
题目大意
看看有没有一个字符串是其他字符串的字串;
解题思路
记录终点用cnt[]数组,枚举每一个字符串然后去搜索,途中找到了一个cnt不为0的结点就做出判断;
这里采用的写法是边插入便判断(就不同去储存了),插入的途中判断有没有那个结点是作为某个个字符串终点的;
这样写有遗漏,因为有可能比他长的字符串在前面插入了,这次它遍历的时候没有找到;那么还应该看看它的子节点是不是全部指向根节点,做出判断;
#include<iostream>
#include<string>
#include<cstring>
using namespace std;
const int N=1e5+100;
struct node{
int Next[10];
int cnt;
int sum;
}trie[N];int id;
bool ans;
void ins(string str)
{
int p=0;
int len=str.size();
for(int i=0;i<len;i++){
int ch=str[i]-'0';
if ( !trie[p].Next[ch] )
trie[p].Next[ch]=++id;
p=trie[p].Next[ch];
if( trie[p].cnt && i<len-1)
ans=0;
}
for(int i=0;i<10;i++){
if( trie[p].Next[i] )
ans=0;
}
trie[p].cnt++;
}
signed main(void)
{
cin.tie(0)->sync_with_stdio(0);
cout.tie(0)->sync_with_stdio(0);
int T;
cin>>T;
while(T--){
memset(trie,0,sizeof(trie) );
ans=1;id=0;
int n;
cin>>n;
for(int i=1;i<=n;i++){
string temp;
cin>>temp;
if(ans)
ins(temp);
}
if( ans )
cout<<"YES\n";
else
cout<<"NO\n";
}
return 0;
}
洛谷P2922-Secret Message G
考点:trie统计前缀
题目大意
一堆暗号和一对信息;输出每个信息队的上多少暗号;
ps:匹配的时候是要注意匹配的是较短的;
解题思路
如果暗号比信息短的话,我们会走到终点;那么思路就是在路径上遇到多少个结尾的结点,就把结尾处的cnt加起来;
如果暗号比信息长的话,我们走不到终点,我们需要知道这个前缀下面还有多少个暗号,也就是需要利用到sum;
sum和cnt交替使用会重复,那么我们记录那个sum-cnt即可;
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
//fstream fs("in.txt",ios::in);
//#define cin fs
const int N=501000;
struct node{
int Next[2];
int cnt;
int sum;
} trie[N];int id;
void ins(string str)
{
int p=0;
for(int i=0;i<str.size();i++){
int ch=str[i]-'0';
if( !trie[p].Next[ch] )
trie[p].Next[ch]=++id;
p=trie[p].Next[ch];
trie[p].sum++;
}
trie[p].cnt++;
}
int Find(string str)
{
int p=0;
int ret=0;
int log=0;
for(int i=0;i<str.size();i++){
int ch=str[i]-'0';
p=trie[p].Next[ch];
log=trie[p].sum-trie[p].cnt;
if( p ){
ret+=trie[p].cnt;
}else{
break;
}
}
return ret+log;
}
int main()
{
cin.tie(0)->sync_with_stdio(0);
cout.tie(0)->sync_with_stdio(0);
int n,m;
cin>>n>>m;;
for(int i=1;i<=n;i++){
int T;
cin>>T;
string str;
while(T--){
int temp;
cin>>temp;
str+='0'+temp;
}
ins(str);
}
for(int i=1;i<=m;i++){
int T;
cin>>T;
string str;
while(T--){
int temp;
cin>>temp;
str+='0'+temp;
}
cout<<Find(str)<<'\n';
}
return 0;
}
HDU1277-全文检索
考点:字符串匹配
题目大意
查询模式串pattern在文本串text中的位置;
解题思路
首先合成一个文本串;然后储存所有的模式串,然后不断取文本串不同的位置去查询,查到了之后就做记录,继续往后查;
#include<iostream>
#include<cstring>
#include<string>
#include<climits>
#include<algorithm>
#include<cmath>
#include<fstream>
#include<vector>
using namespace std;
const int N=5e5+100;//计算节点个数
struct Node{
int Next[10];
int sum;
int index;
int end;
};
class trietree{
private:
int id;
Node node[N];
public:
void Insert(int num,int index=-0x7f7f7f7f);
void Insert(string str,int index=-0x7f7f7f7f);
void Delete(int num);
void Delete(string str);
void reset();
public:
int Search1(int num,int limits);
int Search(string str);
int Max_xor(int num);
int Min_xor(int num);
} trie;
void trietree::Insert(int num,int index)
{
int p=0;
for(int i=30;i>=0;i--){
int ch=(num>>i)&1;
if( !node[p].Next[ch])
node[p].Next[ch]=++id;
p=node[p].Next[ch];
node[p].sum++;
}
node[p].index=index;
}
void trietree::Insert(string str,int index)
{
int p=0;
for(int i=0;i<str.size();i++){
int ch=str[i]-'0';
if( !node[p].Next[ch] )
node[p].Next[ch]=++id;
p=node[p].Next[ch];
node[p].sum++;
}
node[p].end++;
node[p].index=index;
}
void trietree::Delete(int num)
{
int p=0;
for(int i=30;i>=0;i--){
int ch=(num>>i)&1;
p=node[p].Next[ch];
if(!p)
return ;
node[p].sum--;
}
memset( node[p].Next,0,sizeof(node[p].Next) );
}
void trietree::Delete(string str)
{
int p=0;
for(int i=0;i<str.size();i++){
int ch=str[i]-'0';
p=node[p].Next[ch];
if(!p)
return ;
node[p].sum--;
}
memset( node[p].Next,0,sizeof(node[p].Next) );
}
int trietree::Search1(int num,int limits)
{
int p=0;
int temp=0;
int ret=0;
for(int i=30;i>=0;i--){
int ch=(num>>i)&1;
if( node[ node[p].Next[ch^1] ].sum && ( temp|(1<<i) ) < limits ){
temp|=(1<<i);
ret+=node[ node[p].Next[ch] ].sum;
p=node[p].Next[ch^1];
}else{
p=node[p].Next[ch];
}
if(!p)
break;
}
return ret;
}
int trietree::Search(string str)
{
int p=0;
int ret=0;
for(int i=0;i<str.size();i++){
int ch=str[i]-'0';
p=node[p].Next[ch];
if(!p)
break;
if( node[p].index ){
ret=node[p].index;
break;
}
}
return ret;
}
int trietree::Max_xor(int num)
{
int p=0;
int ret=0;
for(int i=30;i>=0;i--){
int ch=(num>>i)&1;
if( node[node[p].Next[ch^1]].sum ){
p=node[p].Next[ch^1];
ret|=(1<<i);
}else{
p=node[p].Next[ch];
}
if(!p)
break;
}
return ret;
}
int trietree::Min_xor(int num)
{
int p=0;
int ret=0;
for(int i=30;i>=0;i--){
int ch=(num>>i)&1;
if( !node[p].Next[ch] || node[p].Next[ch^1] && node[ node[p].Next[ch] ].sum<=1 ){
ch^=1;
ret|=(1<<i);
}
p=node[p].Next[ch];
if(!p)
break;
}
return ret;
}
void trietree::reset()
{
id=0;
memset(node,0,sizeof(node) );
}
void init()
{
cin.tie(0)->sync_with_stdio(0);
cout.tie(0)->sync_with_stdio(0);
}
string text;
vector<int> ans;
bool done[N];
void reinit()
{
trie.reset();
}
signed main(void)
{
init();
reinit();
int n,m;
cin>>n>>m;
string temp;
cin.get();
for(int i=1;i<=n;i++){
getline(cin,temp);
text+=temp;
}
// cout<<text<<'\n';
cin.get();
for(int i=1;i<=m;i++){
getline(cin,temp);
// cout<<temp.substr(temp.find(']')+2)<<'\n';
trie.Insert( temp.substr( temp.find(']',0)+2 ),i );
}
for(int i=0;i<text.size();i++){
int ret=trie.Search( text.substr(i) );
if(!done[ret] && ret) {
done[ret]=1;
ans.push_back(ret);
}
}
if( ans.size() ){
cout<< "Found key:";
for(auto it:ans)
cout<< " [Key No. "<< it<< "]";// [Key No. 9]
cout<<'\n';
}else{
cout<< "No key can be found !";
cout<<'\n';
}
return 0;
}
洛谷P3879-阅读理解
考点:字符串匹配
题目大意
每个生词输出一行,统计其在哪几篇短文中出现过,输出短文编号
解题思路
这里给每一个文本串建一个树,然后枚举每一个字符串在每一个树中的结果;
当然也可以像HDU_1277那样,合成一整个文本串,然后储存生词,枚举文本搜索;
#include<iostream>
#include<string>
using namespace std;
namespace first{
const int N=5e3+100;
int trie[1010][N][26]={0};
int id[N]={0};
int cnt[1010][N]={0};
void insert(string str,int index)
{
int p=0;
for(int i=0;i<str.size();i++){
int ch=str[i]-'a';
if( !trie[index][p][ch] )
trie[index][p][ch]=++id[index];
p=trie[index][p][ch];
}
cnt[index][p]++;
}
int Find(string str,int index)
{
int p=0;
//trie编号
for(int i=0;i<str.size();i++){
int ch=str[i]-'a';
p=trie[index][p][ch];
if( !p )
return 0;
}
return !!cnt[index][p];
}
void solve()
{
cin.tie(0)->sync_with_stdio(0);
cout.tie(0)->sync_with_stdio(0);
int n,m;
cin>>n;
for(int i=1;i<=n;i++){
int T;
cin>>T;
while(T--){
string temp;
cin>>temp;
insert(temp,i);
}
}
cin>>m;
for(int i=1;i<=m;i++){
string temp;
cin>>temp;
bool first=0;
for(int j=1;j<=n;j++)
if( Find(temp,j) ){
if(first)
cout<<' '<<j;
else
cout<<j,first=1;
}
cout<<'\n';
}
}
}
using namespace first;
signed main(void)
{
solve();
return 0;
}
HDU1247-Hat’s Words
考点:字符串匹配
题目大意
给一组词,选三个不一样的词,然后其中两个可以拼成另外一个;
输出可以被拼成的那个词;
解题思路
说什么做什么,这里要求拼单词,那我们就枚举要拼的单词,然后去找可不可以某种方式分解成两个,具体来说就是限制深度为2的dfs去爆搜每一种可能;数据不是很大,但是一直猛WA,理不清逻辑,主要问题就是没有办法保证分解出来的词不重复选用;
反思一下;他们让我们求可以被拼成的单词,我们去分解单词;分解单词是有很多种的,加上不便访问的数据结构,我们没有办法实现这种东西;我们知道三个字符串是都在这一组词里面的,我们分解难做但是组合却很简单,因为选出来的两个东西是确定的,然后组合出来的东西也是确定的;
但如果是真弄两个去枚举的话可能会再次遇到重复的问题;于是我们对存在的字符串进行分解,然后再去查找,从而避免重复的问题;
#include<iostream>
#include<string>
using namespace std;
const int N=5e4+100;
struct node{
int Next[26];
int cnt;
} trie[N];int id;
string str[N];int size__;
void ins(string str)
{
int p=0;
for(int i=0;i<str.size() ;i++){
int ch=str[i]-'a';
if( !trie[p].Next[ch] )
trie[p].Next[ch]=++id;
p=trie[p].Next[ch];
}
trie[p].cnt++;
}
int Find(string str)
{
int p=0;
for(int i=0;i<str.size();i++){
int ch=str[i]-'a';
p=trie[p].Next[ch];
if( !p )
return 0;
}
return trie[p].cnt;
}
signed main(void)
{
cin.tie(0)->sync_with_stdio(0);
cout.tie(0)->sync_with_stdio(0);
for( size__=1; getline(cin,str[size__]) ; size__++ ){
ins(str[size__]);
}
for(int i=1;i<=size__;i++){
for(int j=1;j<str[i].size();j++){
if( Find( str[i].substr(j) ) && Find( str[i].substr(0,j) ) ){
cout<<str[i]<<'\n';
break;
}
}
}
return 0;
}
CodeForces-633C-Spy Syndrome 2
考点:深搜trie
题目大意
通过奇怪的方式加密:
Convert all letters of the sentence to lowercase.
Reverse each of the words of the sentence individually.
Remove all the spaces in the sentence.
然后给出字典,输出原来的句子;
解题思路
按部就班地去搜索不同位置的句子对应不同的单词,一旦失陪就回溯,完成搜索之后直接输出,然后一路退出;
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
using namespace std;
const int N=1.5e6;
struct trietree{
int Next[26];
int index;
}trie[N];int id;
string str[N];int size__;
string text;
vector<int> ans;
int n;
void init()
{
cin.tie(0)->sync_with_stdio(0);
cout.tie(0)->sync_with_stdio(0);
}
void reinit()
{
id=0;size__=0;
memset(trie,0,sizeof(trie) );
}
void ins(string str,int index=-0x7f7f7f7f)
{
int p=0;
for(int i=0;i<str.size();i++){
int ch=str[i]-'a';
if( !trie[p].Next[ch])
trie[p].Next[ch]=++id;
p=trie[p].Next[ch];
}
if( !trie[p].index )
trie[p].index=index;
}
bool dfs(int index)
{
if(index==n){
for(auto it:ans)
cout<<str[it]<<' ';
return true;
}else{
int p=0;
for(int i=index;i<text.size();i++){
int ch=text[i]-'a';
p=trie[p].Next[ch];
if(!p)
break;
if( trie[p].index ){
ans.push_back(trie[p].index);
if(dfs(i+1)){
return true;
}
ans.pop_back();
}
}
return false;
}
}
signed main(void)
{
init();
cin>>n;
cin.get();
getline(cin,text);
int m;cin>>m;
for(int i=1;i<=m;i++){
cin>>str[i];
string temp(str[i]);
reverse(temp.begin(),temp.end());
for(int j=0;j<temp.size();j++){
if( isupper(temp[j]) )
temp[j]+='a'-'A';
}
ins(temp,i);
}
dfs(0);
return 0;
}
POJ3764-The xor-longest Path
考点:01trie维护异或和
题目大意
求出最长的异或路径;
解题思路
预处理转化出异或前缀和;然后按照贪心的思路去选异或数;
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
const int N=1e6+10;
int head[N]={0};
struct edge{
int e;
int w;
int Next;
} var[N];int tot;
struct node{
int Next[2];
} trie[N];int id;
int a[N]={0};
string str[N];
void init()
{
id=0;
tot=0;
memset(a,0,sizeof(a));
memset(var,0,sizeof(var));
memset(trie,0,sizeof(trie));
}
void add(int u,int v,int w)
{
tot++;
var[tot].Next=head[u];
head[u]=tot;
var[tot].e=v;
var[tot].w=w;
}
void dfs(int u,int fa)
{
for(int i=head[u];i;i=var[i].Next){
int to=var[i].e;
if(to!=fa){
a[to]=a[u]^var[i].w;
dfs(to,u);
}
}
}
void ins(string str)
{
int p=0;
for(int i=0;i<str.size();i++){
int ch=str[i]-'0';
if( !trie[p].Next[ch] )
trie[p].Next[ch]=++id;
p=trie[p].Next[ch];
}
}
int Find(string str)
{
int p=0;
int ret=0;//多少位是不同的
for(int i=0;i<str.size();i++){
int ch=str[i]-'0';
ret<<=1;
if( trie[p].Next[ch^1] ) {
p=trie[p].Next[ch^1];
ret|=1;
}else{
p=trie[p].Next[ch];
}
if(!p)
break;
}
return ret;
}
signed main(void)
{
cin.tie(0)->sync_with_stdio(0);
cout.tie(0)->sync_with_stdio(0);
int n;
while(cin>>n){
init();
for(int i=1;i<=n;i++){
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
add(v,u,w);
}
dfs(1,-1);
for(int i=1;i<=n;i++){
int temp=a[i];
while( temp ){
str[i]+=(temp&1)+'0';
temp>>=1;
}
str[i].resize(31,'0');
reverse(str[i].begin(),str[i].end() );
ins(str[i]);
}
int maxn=-1;
for(int i=1;i<=n;i++){
maxn=max( maxn,Find(str[i]) );
}
cout<<maxn<<'\n';
}
return 0;
}
LightOJ-1269-Consecutive Sum
考点:01trie维护异或和
题目大意
求出序列最大最小的异或和区间
解题思路
搜最小值不同于搜最大值,还是按照倒序建立01串进行贪心搜索;但是我们可能会搜到自己身上,我们可以使用delete()减少相应搜索的那个数(如果存在的话)的个数,然后再进行搜索,然后再插入,类似于最后一题--hdu-5536-chip-factory;
我们观察最小值是怎么选出来的;从下往上找的话,我们会发现其实就是在搜索的那个数字(如果有的话),我们回到第一个岔口,搜索另一个结点;自上而下地编码的话,我们就可以先去查找"自己",然后做出"第一次变化";
剩下的按照有没有对应的子节点,能不变就不变的原则向下搜索;
#include<iostream>
#include<cstring>
#include<climits>
#include<cmath>
using namespace std;
const int N=5e4+100;
struct Node{
int Next[2];
int sum;
};
class trietree{
private:
Node node[32*N];int id;
public:
void Insert(int num);
void Insert(string str);
void Delete(int num);
void Delete(string str);
int Max_xor(int num);
int Min_xor(int num);
void Search(int num);
void Search(string str);
void reset();
} trie;
void trietree::Insert(int num)
{
int p=0;
for(int i=30;i>=0;i--){
int ch=(num>>i)&1;
if( !node[p].Next[ch])
node[p].Next[ch]=++id;
p=node[p].Next[ch];
node[p].sum++;
}
}
void trietree::Delete(int num)
{
int p=0;
for(int i=30;i>=0;i--){
int ch=(num>>i)&1;
p=node[p].Next[ch];
}
}
void trietree::Search(int num)
{
int p=0;
for(int i=30;i>=0;i--){
int ch=(num>>i)&1;
p=node[p].Next[ch];
}
}
int trietree::Max_xor(int num)
{
int p=0;
int ret=0;
for(int i=30;i>=0;i--){
int ch=(num>>i)&1;
if( node[p].Next[ch^1] ){
p=node[p].Next[ch^1];
ret|=(1<<i);
}else{
p=node[p].Next[ch];
}
if(!p)
break;
}
return ret;
}
int trietree::Min_xor(int num)
{
int p=0;
int ret=0;
bool flag=1;
for(int i=30;i>=0;i--){
int ch=(num>>i)&1;
if( !node[p].Next[ch] && !node[p].sum || flag && node[p].Next[ch^1] && node[ node[p].Next[ch] ].sum<=1 ){
ch^=1;
ret|=(1<<i);
flag=0;
}
p=node[p].Next[ch];
if(!p)
break;
}
return ret;
}
void trietree::reset()
{
id=0;
memset(node,0,sizeof(node) );
}
void init(){}
int a[N]={0};
void reinit()
{
trie.reset();
trie.Insert(0);
memset(a,0,sizeof(a));
}
signed main(void)
{
int T;
scanf("%d",&T);
for(int t=1;t<=T;t++){
reinit();
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",a+i);
a[i]^=a[i-1];
trie.Insert(a[i]);
}
int maxn=0,minn=INT_MAX;
for(int i=0;i<=n;i++){
maxn=max(maxn,trie.Max_xor(a[i]) );
minn=min(minn,trie.Min_xor(a[i]) );
}
printf("Case %d: %d %d\n",t,maxn,minn);
}
return 0;
}
HDU5687-Problem C
考点: 维护字符串trie
题目大意
维护一个字符串trie
解题思路
主要是delete()的写法问题;这里主要是要操作前缀和统计个数的sum的加减;与此同时,具体的判断也最好使用sum作为依据而不是是否分配点位;
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
const int N=3e6+100;
struct node{
int Next[26];
int cnt;
int sum;
int fa;
} trie[N];int id;
void update(int k)
{
while(trie[k].fa){
trie[ trie[k].fa ].sum = trie[ trie[k].fa ].cnt + trie[k].sum ;
k=trie[k].fa;
}
}
void ins(string str)
{
int p=0;
for(int i=0;i<str.size();i++){
int ch=str[i]-'a';
if( !trie[p].Next[ch] )
trie[p].Next[ch]=++id;//动态开点
trie[ trie[p].Next[ch] ].fa=p;
p=trie[p].Next[ch];
trie[p].sum++;//统计多少个有这个前缀的
}
trie[p].cnt++;//单词标记
}
int Find(string str)
{
int p=0;
for(int i=0;i<str.size();i++){
int ch=str[i]-'a';
p=trie[p].Next[ch];
if( !p )
return 0;
}
return trie[p].sum;
}
void del(string str,int len)
{
if(len<=0)
return ;
int p=0;
for(int i=0;i<str.size();i++){
int ch=str[i]-'a';
p=trie[p].Next[ch];
trie[p].sum-=len;
if( !p )//没有这个前缀
return ;
}
trie[p].sum=trie[p].cnt=0;
memset(trie[p].Next,0,sizeof(trie[p].Next) );
}
int main()
{
cin.tie(0)->sync_with_stdio(0);
cout.tie(0)->sync_with_stdio(0);
int n;
cin>>n;
for(int i=1;i<=n;i++){
string str;
cin>>str;
if( str=="insert"){
cin>>str;
ins(str);
}else if( str=="delete"){
cin>>str;
del(str,Find(str) );
}else if( str=="search"){
cin>>str;
if( Find(str) )
cout<<"Yes\n";
else
cout<<"No\n";
}
}
return 0;
}
CodeForces-817E -Choosing The Commander
考点:维护01trie+搜索统计
题目大意
将军p和士兵p异或小于将军l那就崇拜,反之不然;要求动态维护+统计所有士兵的情况;
解题思路
主要难度在搜索,搜索的难度就主要是怎么找小于限定值的异或值的总数;
其实找刚好小于给定值的那个值就行了,剩下的我们在贪心转移的过程中去加上那些比这个现在已经有了的异或值还小的数的个数,也就是之前的sum;
#include<iostream>
#include<cstring>
#include<climits>
#include<cmath>
using namespace std;
const int N=1e5+100;
struct Node{
int Next[2];
int sum;
};
class trietree{
private:
int id;
Node node[N<<5];
public:
void Insert(int num);
void Insert(string str);
void Delete(int num);
void Delete(string str);
int Search(int num,int limits);
void Search(string str);
void reset();
public:
int Max_xor(int num);
int Min_xor(int num);
} trie;
void trietree::Insert(int num)
{
int p=0;
for(int i=30;i>=0;i--){
int ch=(num>>i)&1;
if( !node[p].Next[ch])
node[p].Next[ch]=++id;
p=node[p].Next[ch];
node[p].sum++;
}
}
void trietree::Delete(int num)
{
int p=0;
for(int i=30;i>=0;i--){
int ch=(num>>i)&1;
p=node[p].Next[ch];
if(!p)
return ;
node[p].sum--;
}
}
int trietree::Search(int num,const int limits)
{
int p=0;
int temp=0;
int ret=0;
for(int i=30;i>=0;i--){
int ch=(num>>i)&1;
if(node[p].Next[ch^1] && node[ node[p].Next[ch^1] ].sum && ( temp|(1<<i) ) < limits ){
temp|=(1<<i);//^上
ret+=node[ node[p].Next[ch] ].sum;//顺着走的数字 ^的数肯定更小
p=node[p].Next[ch^1];// 取反走
}else{//顺着走
p=node[p].Next[ch];
}
if(!p){
break;
}
}
ret+=node[p].sum;
return ret;
}
int trietree::Max_xor(int num)
{
int p=0;
int ret=0;
for(int i=30;i>=0;i--){
int ch=(num>>i)&1;
if( node[p].Next[ch^1] ){
p=node[p].Next[ch^1];
ret|=(1<<i);
}else{
p=node[p].Next[ch];
}
if(!p)
break;
}
return ret;
}
int trietree::Min_xor(int num)
{
int p=0;
int ret=0;
for(int i=30;i>=0;i--){
int ch=(num>>i)&1;
if( !node[p].Next[ch] || node[p].Next[ch^1] && node[ node[p].Next[ch] ].sum<=1 ){
ch^=1;
ret|=(1<<i);
}
p=node[p].Next[ch];
if(!p)
break;
}
return ret;
}
void trietree::reset()
{
id=0;
memset(node,0,sizeof(node) );
}
void init(){}
void reinit()
{
trie.reset();
trie.Insert(0);
}
signed main(void)
{
int T;
scanf("%d",&T);
while(T--){
// reinit();
int op,pi,li;
scanf("%d",&op);
switch(op){
case 1:{
scanf("%d",&pi);
trie.Insert(pi);
break;
}
case 2:{
scanf("%d",&pi);
trie.Delete(pi);
break;
}
case 3:{
scanf("%d %d",&pi,&li);
printf("%d\n",trie.Search(pi,li) );
break;
}
}
}
return 0;
}
HDU-5536-Chip Factory
考点:trie的应用
题目大意
选三个数,两个相加和第三个去异或值,求这么计算出来最大的情况;
解题思路
借由题目要求里面提到的删除操作,我们可以先删,再搜,再加;
#include<iostream>
#include<cstring>
#include<climits>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=5e4+100;
struct Node{
int Next[2];
int sum;
int index;
};
class trietree{
private:
Node node[N];int id;
public:
void Insert(int num,int index=-0x7f7f7f7f);
void Insert(string str,int index=-0x7f7f7f7f);
void Delete(int num);
void Delete(string str);
void reset();
public:
int Search1(int num,int limits);
int Search2(int num);
void Search(string str);
int Max_xor(int num);
int Min_xor(int num);
} trie;
void trietree::Insert(int num,int index)
{
int p=0;
for(int i=30;i>=0;i--){
int ch=(num>>i)&1;
if( !node[p].Next[ch])
node[p].Next[ch]=++id;
p=node[p].Next[ch];
node[p].sum++;
}
}
void trietree::Delete(int num)
{
int p=0;
for(int i=30;i>=0;i--){
int ch=(num>>i)&1;
p=node[p].Next[ch];
node[p].sum--;
if(!p)
return ;
}
}
int trietree::Search1(int num,int limits)
{
int p=0;
int temp=0;
int ret=0;
for(int i=30;i>=0;i--){
int ch=(num>>i)&1;
if( node[ node[p].Next[ch^1] ].sum && ( temp|(1<<i) ) < limits ){
temp|=(1<<i);
ret+=node[ node[p].Next[ch] ].sum;
p=node[p].Next[ch^1];
}else{
p=node[p].Next[ch];
}
if(!p)
break;
}
return ret;
}
int trietree::Max_xor(int num)
{
int p=0;
int ret=0;
for(int i=30;i>=0;i--){
int ch=(num>>i)&1;
if( node[node[p].Next[ch^1]].sum ){
p=node[p].Next[ch^1];
ret|=(1<<i);
}else{
p=node[p].Next[ch];
}
if(!p)
break;
}
return ret;
}
int trietree::Min_xor(int num)
{
int p=0;
int ret=0;
for(int i=30;i>=0;i--){
int ch=(num>>i)&1;
if( !node[p].Next[ch] || node[p].Next[ch^1] && node[ node[p].Next[ch] ].sum<=1 ){
ch^=1;
ret|=(1<<i);
}
p=node[p].Next[ch];
if(!p)
break;
}
return ret;
}
void trietree::reset()
{
id=0;
memset(node,0,sizeof(node) );
}
void init(){}
int b[N]={0};
void reinit()
{
trie.reset();
// trie.Insert(0);
memset(b,0,sizeof(b));
}
signed main(void)
{
int T;
scanf("%d",&T);
while(T--){
reinit();
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",b+i);
trie.Insert(b[i]);
}
sort(b+1,b+1+n);
int maxn=0;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
trie.Delete(b[i]);
trie.Delete(b[j]);
maxn=max(maxn,trie.Max_xor(b[i]+b[j]));
trie.Insert(b[i]);
trie.Insert(b[j]);
}
}
printf("%d\n", maxn );
}
return 0;
}
浙公网安备 33010602011771号