字典树
9.16
A - 统计难题(字典树模板题)
Ignatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).
Input
输入数据的第一部分是一张单词表,每行一个单词,单词的长度不超过10,它们代表的是老师交给Ignatius统计的单词,一个空行代表单词表的结束.第二部分是一连串的提问,每行一个提问,每个提问都是一个字符串.
注意:本题只有一组测试数据,处理到文件结束.
Output
对于每个提问,给出以该字符串为前缀的单词的数量.
Sample Input
banana
band
bee
absolute
acm
ba
b
band
abc
Sample Output
2
3
1
0
字典树的应用,也是单词查找树,Trie树,用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率高。
主要是将前缀一样的字符连接在一起,一层一层查找,图片看这里https://blog.csdn.net/weixin_39778570/article/details/81990417
#include<iostream> #include<cstring> #include<cstdio> using namespace std; const int maxn=500000; char s[20]; struct node { int sum; int ch[26]; node()//这里有一丢丢迷 { sum=0; memset(ch,0,sizeof(ch)); } }tree[maxn]; int ans=0; void insert(char *s) { int x=0; int len=strlen(s); for(int i=0;i<len;i++) { int p=s[i]-'a'; if(tree[x].ch[p]==0) tree[x].ch[p]=++ans; x=tree[x].ch[p]; tree[x].sum++; } } int find(char *s) { int x=0; int len=strlen(s); for(int i=0;i<len;i++) { int p=s[i]-'a'; if(tree[x].ch[p]==0) return 0; x=tree[x].ch[p]; } return tree[x].sum; } int main() { while(gets(s)) { if(strlen(s)==0) break; insert(s);//将单词插入字典树 } while(gets(s)) { printf("%d\n",find(s));//输入前缀,查找 } return 0; }
9.18
A - Xor Sum(字典树+二进制的应用)
Input输入包含若干组测试数据,每组测试数据包含若干行。
输入的第一行是一个整数T(T < 10),表示共有T组数据。
每组数据的第一行输入两个正整数N,M(<1=N,M<=100000),接下来一行,包含N个正整数,代表 Zeus 的获得的集合,之后M行,每行一个正整数S,代表 Prometheus 询问的正整数。所有正整数均不超过2^32。Output对于每组数据,首先需要输出单独一行”Case #?:”,其中问号处应填入当前的数据组数,组数从1开始计算。
对于每个询问,输出一个正整数K,使得K与S异或值最大。Sample Input
2 3 2 3 4 5 1 5 4 1 4 6 5 6 3
Sample Output
Case #1: 4 3 Case #2: 4
这是字典树+二进制的应用,首先,异或就是相同为假,不同为真,题目是求出异或结果的最大值,所以需要转换成二进制,
所有正整数均不超过2^32,所以字典树最高为 32,转换为32位的二进制数即可,如
3就是000000......0011,
4就是000000......0100,
5就是000000......0101,
1就是000000......0001,
如第一个样例:3 4 5,1
前面全部为0,继续找,直到遇到不同的值,因为异或不同为1,相同为0,所以3排除,剩下4,5,继续找,最后一位5与1相同,
所以5排除,所以1和4异或结果最大
#include<iostream> using namespace std; struct node { int t; node *child[2];//0 1 node() { t=0; for(int i=0;i<2;i++) child[i]=NULL;//初始化 } }; node *root; void insert(int x) { int a[32]; int y=x;//标记原数 node *p,*newn; p=root; for(int i=0;i<32;i++)//转换二进制 { a[i]=x&1; x>>=1; } for(int i=31;i>=0;i--) { if(p->child[a[i]]==NULL) { newn=new node; p->child[a[i]]=newn; } p=p->child[a[i]]; } p->t=y; } int find(int x) { int a[32]; node *p; p=root; for(int i=0;i<32;i++) { a[i]=x&1; x>>=1; } for(int i=31;i>=0;i--) { if(p->child[!a[i]]!=NULL)//值不同异或为1,所以要进入不同的分支 p=p->child[!a[i]]; else p=p->child[a[i]]; } return p->t; } int main() { int t,n,m,x,k=1;; cin>>t; while(t--) { cin>>n>>m; root=new node; for(int i=0;i<n;i++) { cin>>x; insert(x); } printf("Case #%d:\n",k++); while(m--) { cin>>x; printf("%d\n",find(x)); } } return 0; }
B - 最大异或和(可持久化字典树)
第一行包含两个整数 N ,M,含义如问题描述所示。
第二行包含 N个非负整数,表示初始的序列 A 。
接下来 M行,每行描述一个操作,格式如题面所述。
假设询问操作有 T个,则输出应该有 T行,每行一个整数表示询问的答案。
Sample InputSample Output
4 5 6
代码是看大佬写的,好难。。。。
可持久化Tire树,关键在于每个节点对于前面的子树可继承,并且利用节点的元素个数来加以区分,可以进行区间查询
#include<iostream> using namespace std; const int maxn=1e6+10; int a[maxn],b[maxn],s[maxn]; int n,m; char ch[5]; struct node { int t; int child[2]; }; struct kcjs { node tr[17000000]; int siz; void insert(int &spc,int rt,int x) { spc=++siz; int root=spc; for(int i=30;~i;i--)//~表示取反,比如i =1101,那么 ~ i 就是0010 { int tmp((x&(1<<i))!=0); tr[root]=tr[rt]; tr[root].t++; rt=tr[rt].child[tmp]; tr[root].child[tmp]=++siz; root=tr[root].child[tmp]; } tr[root].t=tr[rt].t+1; return; } int query(int spc1,int spc2,int v) { int ans=0; int rt1=spc1,rt2=spc2; for(int i=30;~i;i--) { int tmp=((v&(1<<i))!=0); if(tr[tr[rt1].child[tmp^1]].t-tr[tr[rt2].child[tmp^1]].t) { ans|=(1<<i); rt1=tr[rt1].child[tmp^1]; rt2=tr[rt2].child[tmp^1]; } else { rt1=tr[rt1].child[tmp]; rt2=tr[rt2].child[tmp]; } } return ans; } }T; int main() { scanf("%d%d",&n,&m); n++; for(int i=2;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) { b[i]=b[i-1]^a[i]; T.insert(s[i],s[i-1],b[i]); } for(int i=1;i<=m;i++) { scanf("%s",ch); if(ch[0]=='A') { n++; scanf("%d",&a[n]); b[n]=b[n-1]^a[n]; T.insert(s[n],s[n-1],b[n]); } else { int l,r,x; scanf("%d%d%d",&l,&r,&x); printf("%d\n",T.query(s[r],s[l-1],x^b[n])); } } return 0; }
9.20
A - 2048 Game
You are playing a variation of game 2048. Initially you have a multiset ss of nn integers. Every integer in this multiset is a power of two.
You may perform any number (possibly, zero) operations with this multiset.
During each operation you choose two equal integers from ss, remove them from ss and insert the number equal to their sum into ss.
For example, if s={1,2,1,1,4,2,2}s={1,2,1,1,4,2,2} and you choose integers 22 and 22, then the multiset becomes {1,1,1,4,4,2}{1,1,1,4,4,2}.
You win if the number 20482048 belongs to your multiset. For example, if s={1024,512,512,4}s={1024,512,512,4} you can win as follows: choose 512512 and 512512, your multiset turns into {1024,1024,4}{1024,1024,4}. Then choose 10241024 and 10241024, your multiset turns into {2048,4}{2048,4} and you win.
You have to determine if you can win this game.
You have to answer qq independent queries.
Input
The first line contains one integer qq (1≤q≤1001≤q≤100) – the number of queries.
The first line of each query contains one integer nn (1≤n≤1001≤n≤100) — the number of elements in multiset.
The second line of each query contains nn integers s1,s2,…,sns1,s2,…,sn (1≤si≤2291≤si≤229) — the description of the multiset. It is guaranteed that all elements of the multiset are powers of two.
Output
For each query print YES if it is possible to obtain the number 20482048 in your multiset, and NO otherwise.
You may print every letter in any case you want (so, for example, the strings yEs, yes, Yes and YES will all be recognized as positive answer).
Example
6 4 1024 512 64 512 1 2048 3 64 512 2 2 4096 4 7 2048 2 2048 2048 2048 2048 2048 2 2048 4096
YES YES NO NO YES YES
Note
In the first query you can win as follows: choose 512512 and 512512, and ss turns into {1024,64,1024}{1024,64,1024}. Then choose 10241024 and 10241024, and ss turns into {2048,64}{2048,64} and you win.
In the second query ss contains 20482048 initially.
其实这道题就是一个思维题,找2048数即可,起初看完题陷入了一个思维误区,觉得得用一些比较复杂的数组或容器来判断两两组合的数可以成为2048.
磨蹭了好久没做出来,后来重新思考,其实没那么复杂,小于2048的数相加如果和大于等于它就满足条件了
#include<iostream> using namespace std; const long long maxn=1e6+10; int a[maxn],x; int flag; int main() { int t; cin>>t; while(t--) { flag=0; cin>>x; for(int i=0;i<x;i++) { cin>>a[i]; } int sum=0; for(int i=0;i<x;i++) { if(a[i]==2048) { flag=1; break; } if(a[i]>2048) continue; else { sum+=a[i]; if(sum>=2048) flag=1; } } if(flag) cout<<"YES"<<endl; else cout<<"NO"<<endl; } return 0; }