能力有限,只会最浅的.
一、Trie常识
字典树空间复杂度: O ( N × ∑ ) O(N\times \sum) O(N×∑) N N N是所有串长度之和, ∑ \sum ∑ 是字符集大小。
定义Trie
struct Trie{
int son[M];bool ed=false; //这里的M就是字符集大小
}a[N]; //N就是所有串长度之和
int tot; //用来记录结点个数
a [ u ] . s o n [ c ] a[u].son[c] a[u].son[c] 表示结点 u u u的儿子 c c c的编号。
a [ u ] . e d a[u].ed a[u].ed 用来判断该结点是否为某个字符串的尾结点。
插入字符串
void ins(char *s){
int u=0,l=strlen(s+1);
for(int i=1;i<=l;i++){
int c=s[i]-'a';
if(!a[u].son[c]) a[u].son[c]=++tot;
u=a[u].son[c];
}
a[u].ed=true;
}
查询某个字符串是否出现
bool query(char *s){
int u=0,l=strlen(s+1);
for(int i=1;i<=l;i++){
int c=s[i]-'a';
if(!a[u].son[c]) return false;
u=a[u].son[c];
}
return true;
}
HDU1671
判断是否有一串是另一串前缀。
插入时判断是否出现结尾标记或者结尾处仍有儿子。
时间复杂度: O ( n l ) O(nl) O(nl) l l l为单个字符串长度
// Problem: UVA11362 Phone List
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/UVA11362
// Memory Limit: 0 MB
// Time Limit: 5000 ms
// Date: 2021-07-28 09:18:07
// --------by Herio--------
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=1e4+5,M=1e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define IOS ios::sync_with_stdio(false),cin.tie(0)
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\n",a[n]);
}
struct Trie{
int son[10],ed=0;
}a[N];
int ok,tot;
char s[11];
void ins(char *s){
int u=0;
for(int i=1;s[i];i++){
int c=s[i]-'0';
if(!a[u].son[c]) a[u].son[c]=++tot;
u=a[u].son[c];
if(a[u].ed) ok=1;
}
for(int i=0;i<10;i++) if(a[u].son[i]) ok=1;
a[u].ed=1;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;scanf("%d",&n);ok=tot=0;mst(a,0);
for(int i=0;i<n;i++){
scanf("%s",s+1);
ins(s);
}
puts(ok?"NO":"YES");
}
return 0;
}
P2292 [HNOI2004]L语言
对单词倒着建Trie树,然后维护 f f f数组, f i f_i fi表示前缀 i i i是否可行。
然后就是普通dp了。
// Problem: P2292 [HNOI2004]L语言
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2292
// Memory Limit: 128 MB
// Time Limit: 1000 ms
// Date: 2021-07-28 10:22:23
// --------by Herio--------
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=2e6+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define IOS ios::sync_with_stdio(false),cin.tie(0)
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\n",a[n]);
}
char buf[1<<21],*p1=buf,*p2=buf;
template <typename T>
inline T& read(T& r) {
r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
return r = w ? -r : r;
}
#define il inline
struct Trie{
int s[26];bool ed=false;
}a[205];
int n,m,tot;
il void ins(char *s){
int u=0,l=strlen(s+1);
for(int i=l;i;i--){
int c=s[i]-'a';
if(!a[u].s[c]) a[u].s[c]=++tot;
u=a[u].s[c];
}
a[u].ed=true;
}
char s[12],t[N];
bool f[N];
int main(){
read(n),read(m);
while(n--){
scanf("%s",s+1);ins(s);
}
while(m--){
f[0]=true;
scanf("%s",t+1);int l=strlen(t+1);
for(int i=1;i<=l;i++){
f[i]=false;int u=0;
for(int j=i;j;j--){
int c=t[j]-'a';
if(!a[u].s[c]) break;
u=a[u].s[c];
if(a[u].ed){
f[i]|=f[j-1];
if(f[i]) break;
}
}
}
int j;
for(j=l;j;j--) if(f[j]) break;
printf("%d\n",j);
}
return 0;
}
P2922 [USACO08DEC]Secret Message G
给定若干模式串,然后问每个文本串与多少个模式串前缀匹配,前缀就是两者长度较小的字符串。
对模式串建Trie,维护每个结点经过的次数和结尾标记。
然后就比较简单了,注意特判下比该文本串长的模式串个数。
// Problem: P2922 [USACO08DEC]Secret Message G
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2922
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Date: 2021-07-06 15:48:40
// --------by Herio--------
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=5e4+5,M=5e5+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define IOS ios::sync_with_stdio(false),cin.tie(0)
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\n",a[n]);
}
struct node{
int nt[2],s,ed;
}a[M];
int b[N],len,cnt=1;
void ins(){
int p=1;
for(int j=1;j<=len;j++){
if(!a[p].nt[b[j]]) a[p].nt[b[j]]=++cnt;
p=a[p].nt[b[j]];
a[p].s++;
}
a[p].ed++;
}
ll que(){
ll ans=0;
int p=1;
bool ok=true;
for(int i=1;i<=len;i++){
if(!a[p].nt[b[i]]){
ok=0;break;
}
p=a[p].nt[b[i]];
ans+=a[p].ed;
}
return ok?ans+a[p].s-a[p].ed:ans;
}
int main(){
int n,m;scanf("%d%d",&m,&n);
for(int i=1;i<=m;i++){
scanf("%d",&len);
for(int j=1;j<=len;j++) scanf("%d",&b[j]);
ins();
}
for(int i=1;i<=n;i++){
scanf("%d",&len);
for(int j=1;j<=len;j++) scanf("%d",&b[j]);
printf("%lld\n",que());
}
return 0;
}
二、01-Trie
https://harris.blog.csdn.net/article/details/107586527
我的上面这篇文章讲过,这里再回顾一下。
01-Trie还是利用Trie+贪心。
从高位往低位建Trie
P4551 最长异或路径
dfs预处理出根到其他点异或和 f i f_i fi,然后就是01-Trie 的裸题了.
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=3e6+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
int n,son[M][2],f[N],h[N],cnt,tot;
struct node{
int to,nt,w;
}e[N<<1];
void add(int u,int v,int w){
e[++tot]={v,h[u],w},h[u]=tot;
e[++tot]={u,h[v],w},h[v]=tot;
}
void dfs(int u,int fa){
for(int i=h[u];i;i=e[i].nt){
if(e[i].to==fa) continue;
int v=e[i].to;
f[v]=f[u]^e[i].w;
dfs(v,u);
}
}
void ins(int x){
int p=0;
for(int i=30;~i;i--){
int &s=son[p][x>>i&1];
if(!s) s=++cnt;
p=s;
}
}
int que(int x){
int p=0,ans=0;
for(int i=30;~i;i--){
int s=x>>i&1;
if(son[p][!s]) ans+=1<<i,p=son[p][!s];
else p=son[p][s];
}
return ans;
}
int main(){
scanf("%d",&n);
for(int i=1,u,v,w;i<n;i++){
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
dfs(1,0);int ans=0;
for(int i=1;i<=n;i++){
ans=max(ans,que(f[i]));
ins(f[i]);
}
printf("%d\n",ans);
return 0;
}
P4735 最大异或和
结尾插入结点,查询区间最大异或和。
可持久化01-Trie
每个前缀异或和维护一棵Trie。
查询就是第 [ l − 1 , r − 1 ] [l-1,r-1] [l−1,r−1]的前缀和异或和与 s u m ⊕ x sum\oplus x sum⊕x的最大异或值。
为了方便,我们把所有位置右移动。
先插入 0 0 0这个值作为第一棵树,这样 [ l − 1 , r − 1 ] → [ l , r ] [l-1,r-1]\rightarrow [l,r] [l−1,r−1]→[l,r]
又因为是差分的性质,我们查询 [ l , r ] [l,r] [l,r]区间的树,所以要访问区间 [ l − 1 , r ] [l-1,r] [l−1,r]。
空间要开 2 e 7 2e7 2e7。
具体见code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=2e7+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define IOS ios::sync_with_stdio(false),cin.tie(0)
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\n",a[n]);
}
char buf[1<<21],*p1=buf,*p2=buf;
template <typename T>
inline T& read(T& r) {
r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
return r = w ? -r : r;
}
int n,q,sum,s[N][2],sz[N],rt[N],tot,cnt;
inline void ins(int x){
int p=rt[cnt];//上一棵Trie 的根结点编号p
rt[++cnt]=++tot;//新建一棵树的根结点,编号为tot
for(int i=23;~i;i--){
int c=x>>i&1;
sz[tot]=sz[p]+1;//在前一棵树的基础上更新树大小.
s[tot][!c]=s[p][!c];//另一个子树直接继承上一个版本.
s[tot][c]=tot+1;++tot;//新建结点,注意这里不要缩写为一步,优先级问题.
p=s[p][c];//上一颗树继续往下走.
}
sz[tot]=sz[p]+1;//结尾的size更新.
}
inline void que(int l,int r,int x){
int lc=rt[l],rc=rt[r],ans=0;//找到第lc棵树,第rc棵树.
for(int i=23;~i;i--){
int c=x>>i&1;
//如果还有结点往该放方向走
if(sz[s[rc][!c]]>sz[s[lc][!c]]) ans^=1<<i,lc=s[lc][!c],rc=s[rc][!c];
else lc=s[lc][c],rc=s[rc][c];
}
printf("%d\n",ans);return;
}
int main(){
read(n),read(q);ins(0);//要插入0,rt[1]=1,0是第一棵树.
for(int i=0,x;i<n;i++)
read(x),sum^=x,ins(sum);
while(q--){
char ch;
scanf("\n%c",&ch);
if(ch=='A'){
int x;read(x),sum^=x,ins(sum);
}
else {
int l,r,x;//注意询问的查询的rt[l]是第l-1棵树,因为0是第一棵树.
//所以此时的区间[l-2,r-1]
read(l),read(r),read(x);
que(l-1,r,sum^x);
}
}
return 0;
}
P5283 [十二省联考2019]异或粽子
求前 k k k大区间异或和 的和.
01-Trie+大根堆
先把上三角转换为矩形,相同的异或不管,因为值为0,不会影响结果.
预处理出每个前缀异或和 s i s_i si与整个区间的最大异或和,丢进大根堆.
然后每次取最大的,加上贡献,再求该 s i s_i si与整个区间第 r k + 1 rk+1 rk+1大,丢进大根堆.
注意求到 r k = n rk=n rk=n 就不要再丢进了.
时间复杂度: O ( ( n + k ) l o g a l o g n ) O((n+k)loga logn) O((n+k)logalogn)
// Problem: P5283 [十二省联考2019]异或粽子
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P5283
// Memory Limit: 1 MB
// Time Limit: 3500 ms
// Date: 2021-07-28 15:37:35
// --------by Herio--------
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=5e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define IOS ios::sync_with_stdio(false),cin.tie(0)
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\n",a[n]);
}
//if have char input #define should cancel
#define getchar()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
template <typename T>
inline T& read(T& r) {
r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
return r = w ? -r : r;
}
int n,k,s[N*30][2],sz[N*30],tot;
inline void ins(ll x){
int u=0;
for(int i=31;~i;i--){
int c=x>>i&1;
if(!s[u][c]) s[u][c]=++tot;
u=s[u][c];
sz[u]++;
}
}
inline ll que(ll x,int k){
ll ans=0;int u=0;
for(int i=31;~i;i--){
int c=x>>i&1;
if(sz[s[u][!c]]>=k) ans^=(1LL<<i),u=s[u][!c];
else k-=sz[s[u][!c]],u=s[u][c];
}
return ans;
}
ll a[N];
struct node{
int id,rk;ll v;
bool operator<(const node &a)const{
return v<a.v;
}
};
priority_queue<node>q;
int main(){
read(n),read(k),ins(0);k<<=1;
for(int i=1;i<=n;i++)
read(a[i]),a[i]^=a[i-1],ins(a[i]);
for(int i=0;i<=n;i++) q.push({i,1,que(a[i],1)});
ll ans=0;
while(k--){
node u=q.top();q.pop();
ans+=u.v;
if(u.rk<n) q.push({u.id,u.rk+1,que(a[u.id],u.rk+1)});
}
printf("%lld\n",ans>>1);
return 0;
}
此题还有个不依赖 k k k的解法, 见CF241B Friends,还不会.