BZOJ3906 : Trie

将输入的Trie建成AC自动机,并建出fail树。

那么操作1等价于在给定点的子树的并集里都加1。

操作2等价于查询给定点到根节点路径的并集的权值和。

求出DFS序后,对于操作1,将点按进入时间戳从小到大排序,然后求出并集,进行区间修改即可。

对于操作2,构造给定点集的虚树,在虚树的每一条边上询问权值和,累加起来即可。

对于子树修改,链查询,可以使用4棵树状数组维护。

时间复杂度$O((m+k)\log n)$。

#include<cstdio>
#include<algorithm>
#define N 100010
using namespace std;
typedef long long ll;
int Case,n,Q,i,j,x,op,ch[N][26],f[N],g[N],nxt[N];
int d[N],size[N],son[N],top[N],st[N],en[N],dfn;
int m,q[N],a[N],tot,t,vis[N];
struct BIT{
  int n,s[N<<1],a[N<<1];ll b[N<<1];
  inline void init(int x){n=x;for(int i=1;i<=n;i++)a[i]=b[i]=s[i]=0;}
  inline void modify(int x,int p){for(int i=x;i<=n;i+=i&-i)a[i]+=p,b[i]+=p*s[x-1];}
  inline ll ask(int x){
    int t0=0;ll t1=0;
    for(int i=x;i;i-=i&-i)t0+=a[i],t1+=b[i];
    return 1LL*s[x]*t0-t1;
  }
  inline void add(int x,int y){modify(x,1),modify(y+1,-1);}
}ti,to;
inline void read(int&a){char c;while(!(((c=getchar())>=‘0‘)&&(c<=‘9‘)));a=c-‘0‘;while(((c=getchar())>=‘0‘)&&(c<=‘9‘))(a*=10)+=c-‘0‘;}
inline int getch(){char c;while(!(((c=getchar())>=‘a‘)&&(c<=‘z‘)));return c;}
inline void make(){
  int h=1,t=0,i,j,x;
  for(i=0;i<26;i++)if(ch[1][i])f[q[++t]=ch[1][i]]=1;else ch[1][i]=1;
  while(h<=t)for(x=q[h++],i=0;i<26;i++)if(ch[x][i])f[ch[x][i]]=ch[f[x]][i],q[++t]=ch[x][i];else ch[x][i]=ch[f[x]][i];
}
void dfs(int x){
  d[x]=d[f[x]]+1,size[x]=1,son[x]=0;
  for(int i=g[x];i;i=nxt[i]){
    dfs(i),size[x]+=size[i];
    if(size[i]>size[son[x]])son[x]=i;
  }
}
void dfs2(int x,int y){
  top[x]=y;st[x]=++dfn;
  if(son[x])dfs2(son[x],y);
  for(int i=g[x];i;i=nxt[i])if(i!=son[x])dfs2(i,i);
  en[x]=++dfn;
}
inline int lca(int x,int y){
  for(;top[x]!=top[y];x=f[top[x]])if(d[top[x]]<d[top[y]])swap(x,y);
  return d[x]<d[y]?x:y;
}
inline int cmp(int x,int y){return st[x]<st[y];}
int main(){
  read(Case);
  while(Case--){
    read(n);
    for(i=2;i<=n;i++)read(x),ch[x][getch()-‘a‘]=i;
    make();
    for(i=2;i<=n;i++)nxt[i]=g[f[i]],g[f[i]]=i;
    dfs(1),dfs2(1,1);
    ti.init(dfn),to.init(dfn);
    for(i=1;i<=n;i++)ti.s[st[i]]=1,to.s[en[i]]=-1;
    for(i=2;i<=dfn;i++)ti.s[i]+=ti.s[i-1],to.s[i]+=to.s[i-1];
    read(Q);
    while(Q--){
      read(op),read(m);
      if(op==1){
        for(i=0;i<m;i++)read(a[i]);
        for(sort(a,a+m,cmp),j=i=0;i<m;i++)if(st[a[i]]>j){
          ti.add(st[a[i]],en[a[i]]);
          to.add(st[a[i]],en[a[i]]);
          j=en[a[i]];
        }
      }else{
        for(tot=i=0;i<m;i++){
          read(x);
          if(!vis[x])vis[a[++tot]=x]=1;
        }
        if(!vis[1])vis[a[++tot]=1]=1;
        m=tot,sort(a+1,a+m+1,cmp);
        for(i=1;i<m;i++)if(!vis[x=lca(a[i],a[i+1])])vis[a[++tot]=x]=1;
        m=tot,sort(a+1,a+m+1,cmp);
        ll ans=ti.ask(1);
        for(q[t=1]=a[1],i=2;i<=m;q[++t]=a[i++]){
          while(st[a[i]]<st[q[t]]||en[a[i]]>en[q[t]])t--;
          ans+=ti.ask(st[a[i]])+to.ask(st[a[i]])-ti.ask(st[q[t]])-to.ask(st[q[t]]);
        }
        for(i=1;i<=m;i++)vis[a[i]]=0;
        printf("%lld\n",ans);
      }
    }
    for(dfn=0,i=1;i<=n;i++)for(f[i]=g[i]=j=0;j<26;j++)ch[i][j]=0;
  }
  return 0;
}

  

时间: 2024-10-18 04:44:15

BZOJ3906 : Trie的相关文章

HDU 1075 What Are You Talking About (Trie树)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1075 map可以过...我上的字典树,小bug有点尴尬,题目没有明确给出数据范围也是无奈. 贡献了几次RE 一次WA.尴尬.discuss里面有个说注意前缀的到是给了点tip.总体来说不错 代码: 1 #define _CRT_SECURE_NO_WARNINGS 2 #include <functional> 3 #include <algorithm> 4 #include <

POJ2778 DNA Sequence Trie+矩阵乘法

题意:给定N个有A C G T组成的字符串,求长度为L的仅由A C G T组成的字符串中有多少个是不含给定的N个字符串的题解: 首先我们把所有的模式串(给定的DNA序列)建Trie,假定我们有一个匹配串,并且在匹配过程到S[i]这个字符时匹配到了Trie上的某个节点t,那么有两种可能: 匹配失败:t->child[S[i]]为空,跳转到t->fail,因此t->fail一定不能是某个模式串的结尾: 匹配成功:跳转到t->child[S[i+1]],因此t->child[S[i

poj3630 Phone List (trie树模板题)

Phone List Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 26328   Accepted: 7938 Description Given a list of phone numbers, determine if it is consistent in the sense that no number is the prefix of another. Let's say the phone catalogu

从Trie谈到AC自动机

ZJOI的SAM让我深受打击,WJZ大神怒D陈老师之T3是SAM裸题orz...我还怎么混?暂且写篇`从Trie谈到AC自动机`骗骗经验. Trie Trie是一种好玩的数据结构.它的每个结点存的是字母,因此得名`字母树`. 出一张图让大家感受下. (image powered by SaiBu NaoCu) 上面那是一棵插入了 ape,app,applicant,application,bake,ban,banana 等词的Trie.红色结点表示接受态. 显然,查找时只需顺着链照下来,插入只需

【BZOJ2741】【块状链表+可持久化trie】FOTILE模拟赛L

Description FOTILE得到了一个长为N的序列A,为了拯救地球,他希望知道某些区间内的最大的连续XOR和. 即对于一个询问,你需要求出max(Ai xor Ai+1 xor Ai+2 ... xor Aj),其中l<=i<=j<=r. 为了体现在线操作,对于一个询问(x,y): l = min ( ((x+lastans) mod N)+1 , ((y+lastans) mod N)+1 ).r = max ( ((x+lastans) mod N)+1 , ((y+last

[算法系列之二十]字典树(Trie)

一 概述 又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种.典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计. 二 优点 利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希表高. 三 性质 (1)根节点不包含字符,除根节点外每一个节点都只包含一个字符: (2)从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串: (3)每个节点的所有子节点包含的字符都不相同. 单词列表为"apps&

跳跃表,字典树(单词查找树,Trie树),后缀树,KMP算法,AC 自动机相关算法原理详细汇总

第一部分:跳跃表 本文将总结一种数据结构:跳跃表.前半部分跳跃表性质和操作的介绍直接摘自<让算法的效率跳起来--浅谈"跳跃表"的相关操作及其应用>上海市华东师范大学第二附属中学 魏冉.之后将附上跳跃表的源代码,以及本人对其的了解.难免有错误之处,希望指正,共同进步.谢谢. 跳跃表(Skip List)是1987年才诞生的一种崭新的数据结构,它在进行查找.插入.删除等操作时的期望时间复杂度均为O(logn),有着近乎替代平衡树的本领.而且最重要的一点,就是它的编程复杂度较同类

Trie树学习2

数组实现的Trie树 字符容量有限,可以使用链表实现更为大容量的Trie #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <vector> #include <map> #include <set> #include <algorithm> #include <cstdlib> #

trie树(字典树)

1. trie树,又名字典树,顾名思义,它是可以用来作字符串查找的数据结构,它的查找效率比散列表还要高. trie树的建树: 比如有字符串"ab" ,"adb","adc"   可以建立字典树如图: 树的根节点head不存储信息,它有26个next指针,分别对应着字符a,b,c等.插入字符串ab时,next['a'-'a']即next[0]为空,这是申请一个结点放在next[0]的位置,插入字符串db时,next['d'-'a']即next[3]