bzoj 2555 SubString——后缀自动机+LCT

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2555

要维护 right 集合的大小。因为 fa 会变,且 fa 构成一棵树,所以考虑用 LCT 来维护……

和平常写的 LCT 不太一样。因为要的值是原树上子树里的值,所以没有 makeroot ,splay 里不维护 splay 里的子树信息,只维护加法标记,表示 link 一下就给原树的自己到根的那条链上的所有点加了自己的值。cut 就是减掉自己的值。所以 query 或者 splay 的时候先把自己这棵 splay 里自己到根的路径 pshd 一遍,且没有 pshp 什么的。而且 link 时要区分两个点哪个是原树上的父亲。

不知怎么看出字符只有大写字母的。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1200005,K=30,M=3e6+5;
int cnt=1,lst=1,go[N][K],fa[N],l[N],mask;
int c[N][2],pre[N],sta[N],top,sm[N],tg[N];
char ch[M];
void decodeWithMask(int n,int mask)
{
  for(int i=0;i<n;i++)
    {
      mask=(mask*131+i)%n;
      swap(ch[i],ch[mask]);
    }
}
bool isroot(int x){return c[pre[x]][0]!=x&&c[pre[x]][1]!=x;}
void pshd(int x)
{
  if(!tg[x])return;int ls=c[x][0],rs=c[x][1];
  sm[ls]+=tg[x];tg[ls]+=tg[x];sm[rs]+=tg[x];tg[rs]+=tg[x];
  tg[x]=0;
}
void Pshd(int x)
{
  sta[top=1]=x;
  for(;!isroot(x);x=pre[x])sta[++top]=pre[x];
  for(int i=top;i;i--)pshd(sta[i]);
}
void rotate(int x)
{
  int y=pre[x],z=pre[y];
  if(!isroot(y))c[z][y==c[z][1]]=x;
  pre[x]=z;
  int d=(x==c[y][1]);
  pre[c[x][!d]]=y; pre[y]=x;
  c[y][d]=c[x][!d]; c[x][!d]=y;
}
void splay(int x)
{
  Pshd(x);
  while(!isroot(x))
    {
      int y=pre[x],z=pre[y];
      if(!isroot(y))
    {
      if((y==c[z][0])^(x==c[y][0]))rotate(x);
      else rotate(y);
    }
      rotate(x);
    }
}
void access(int x)
{
  for(int t=0;x;splay(x),c[x][1]=t,t=x,x=pre[x]);
}
void link(int x,int y)
{
  pre[y]=x;access(x);splay(x);
  sm[x]+=sm[y]; tg[x]+=sm[y];//tg:pre of x all added
}
void cut(int x)
{
  access(x);splay(x);
  sm[c[x][0]]-=sm[x]; tg[c[x][0]]-=sm[x];//tg:pre of x all declined
  pre[c[x][0]]=0; c[x][0]=0;
}
void add(int w)
{
  int p=lst,np=++cnt;lst=np;l[np]=l[p]+1;sm[np]=1;
  for(;p&&!go[p][w];p=fa[p])go[p][w]=np;
  if(!p)fa[np]=1,link(1,np);
  else
    {
      int q=go[p][w];
      if(l[q]==l[p]+1)fa[np]=q,link(q,np);
      else
    {
      int nq=++cnt;l[nq]=l[p]+1;
      cut(q);link(fa[q],nq);link(nq,q);link(nq,np);///
      fa[nq]=fa[q];fa[q]=nq;fa[np]=nq;//after link&cut !
      memcpy(go[nq],go[q],sizeof go[q]);
      for(;go[p][w]==q;p=fa[p])go[p][w]=nq;
    }
    }
}
int query(int len)
{
  int cr=1;
  for(int i=0;i<len;i++)
    {
      if(!go[cr][ch[i]-‘A‘+1])return 0;
      cr=go[cr][ch[i]-‘A‘+1];
    }
  Pshd(cr); return sm[cr];
}
int main()
{
  int Q;scanf("%d",&Q);scanf("%s",ch);
  int n=strlen(ch);for(int i=0;i<n;i++)add(ch[i]-‘A‘+1);
  char tch[10];
  while(Q--)
    {
      scanf("%s",tch);scanf("%s",ch);n=strlen(ch);
      decodeWithMask(n,mask);
      if(tch[0]==‘A‘)
    {
      for(int i=0;i<n;i++)add(ch[i]-‘A‘+1);
    }
      else
    {
      int d=query(n);printf("%d\n",d);
      mask^=d;
    }
    }
  return 0;
}

原文地址:https://www.cnblogs.com/Narh/p/10107574.html

时间: 2024-08-28 13:58:51

bzoj 2555 SubString——后缀自动机+LCT的相关文章

bzoj 2555 SubString —— 后缀自动机+LCT

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2555 建立后缀自动机,就可以直接加入新串了: 出现次数就是 Right 集合的大小,需要查询 Parent 树上的子树和: 所以可以用 LCT 维护 Parent 树,因为 Parent 树是有根树所以不需要 makeroot: 代码中的两种 cut 写法都可以,其实这里的 splay 节点上记的 siz 值不是 splay 子树里的而是原子树( Parent 树上)里的: 注意读入的函数

BZOJ 2555: SubString [后缀自动机 LCT]

2555: SubString Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 2045  Solved: 583[Submit][Status][Discuss] Description 懒得写背景了,给你一个字符串init,要求你支持两个操作        (1):在当前字符串的后面插入一个字符串        (2):询问字符串s在当前字符串中出现了几次?(作为连续子串)        你必须在线支持这些操作. Input 第一行一个数Q表示

2555: SubString 后缀自动机+LCT

2555: SubString Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 688  Solved: 235[Submit][Status][Discuss] Description 懒得写背景了,给你一个字符串init,要求你支持两个操作          (1):在当前字符串的后面插入一个字符串          (2):询问字符串s在当前字符串中出现了几次?(作为连续子串)          你必须在线支持这些操作. Input 第一行一

BZOJ 2555 SubString 后缀自动机

题目大意:给出一个字符串,支持在线在字符串后面加一个字符串,查询一个字符串在串中出现过几次. 思路:如果不想写正解的话,这个题就是后缀自动机的简单应用.正解其实是LCT+SAM,但是时间比暴力慢一倍... 暴力就很简单了,正序建立后缀自动机,每次查询的时候找到位置直接输出size的值.注意两点,一个是分裂节点的时候,size也要复制过去.查询的时候发现找不到要return 0; CODE: #include <cstdio> #include <cstring> #include

BZOJ 2555 Substring 后缀自动机+Link-Cut-Tree

题目大意:给定一个初始字符串,提供两种操作: 1.在这个字符串的后面连接一个字符串 2.询问某个字符串在当前串中出现了多少次 SAM大叔的自动机~~ 对于每个询问就是在后缀自动机上找到该子串所对应的节点 找不到返回0 然后这个节点的Right集合的大小就是这个子串的出现次数 每次Extend的时候将新建节点沿着parent指针到根的路径上所有点的Right集合大小+1即可 分裂节点的时候要将Right集合一并复制 这方法虽然暴力但是非常有效 由于parent是一棵树,所以可以用LCT来维护这棵树

bzoj 2555 SubString(SAM+LCT)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2555 [题意] 给定一个字符串,可以随时插入字符串,提供查询s在其中作为连续子串的出现次数. [思路] 子串的出现次数,这使我们想到了后缀自动机,如果没有插入操作,则出现次数为字符串对应节点|right|集的大小. Right的递推方法为:|fa->right| <- |right| 因为需要不断地插入字符串,所以parent树会发生变化,我们考虑使用LCT维护parent树.SA

【BZOJ2555】SubString 后缀自动机+LCT

[BZOJ2555]SubString Description 懒得写背景了,给你一个字符串init,要求你支持两个操作         (1):在当前字符串的后面插入一个字符串         (2):询问字符串s在当前字符串中出现了几次?(作为连续子串)         你必须在线支持这些操作. Input 第一行一个数Q表示操作个数         第二行一个字符串表示初始字符串init         接下来Q行,每行2个字符串Type,Str         Type是ADD的话表示在

●BZOJ 2555 SubString

题链: http://www.lydsy.com/JudgeOnline/problem.php?id=2555题解: 后缀自动机+LCT 不难发现,对于输入的询问串,在自动机里trans后的到的状态的Right集合的大小就是答案. 那么后缀自动机本身就是支持在线添加的,问题就是如何维护好parent树,即如何维护好每个状态的Right集合. 那么Link-Cut-Tree就显然可以完成动态维护parent树的任务. (这里是维护的一颗根固定的树,没有Beroot()等换根函数) 代码: #in

字符串(LCT,后缀自动机):BZOJ 2555 SubString

2555: SubString Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 1620  Solved: 471 Description 懒得写背景了,给你一个字符串init,要求你支持两个操作          (1):在当前字符串的后面插入一个字符串          (2):询问字符串s在当前字符串中出现了几次?(作为连续子串)          你必须在线支持这些操作. Input 第一行一个数Q表示操作个数          第二行一个