bzoj 3881 [Coci2015]Divljak——LCT维护parent树链并

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

对 S 建 SAM ,每个 T 会让 S 的 parent 树的链并答案+1;在 T 走每一步的时候,走到的节点用 LCT access 一下,就能找到该点到 parent 根的链。

给链打标记。在 access 的过程中,如果遇到已经打过这个 T 标记的点,就停止 access 。

注意实现的时候,在判断 fa[x] 有没有标记之前要先 splay(fa[x]) 。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ls c[cr][0]
#define rs c[cr][1]
using namespace std;
int rdn()
{
  int ret=0;bool fx=1;char ch=getchar();
  while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)fx=0;ch=getchar();}
  while(ch>=‘0‘&&ch<=‘9‘)ret=ret*10+ch-‘0‘,ch=getchar();
  return fx?ret:-ret;
}
const int N=1e5+5,M=2e6+5,K=26;
int n,ps[N],tot=1,c[M][K],tc[M][K],fl[M],q[M];
int tim,dfn[M],fa[M],vl[M],tg[M],sta[M];
char s[M];
bool isrt(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
void cz(int cr)
{
  if(!tg[cr])return; int w=tg[cr];tg[cr]=0;
  vl[ls]+=w; vl[rs]+=w;
  tg[ls]+=w; tg[rs]+=w;
  dfn[ls]=dfn[rs]=dfn[cr];///
}
void rotate(int x)
{
  int y=fa[x],z=fa[y],d=(x==c[y][1]);
  if(!isrt(y))c[z][y==c[z][1]]=x;
  fa[x]=z; fa[y]=x; fa[c[x][!d]]=y;
  c[y][d]=c[x][!d]; c[x][!d]=y;
}
void splay(int x)
{
  int top; sta[top=1]=x;
  for(int k=x;!isrt(k);k=fa[k])sta[++top]=fa[k];
  for(int i=top;i;i--)cz(sta[i]);
  for(int y=fa[x],z=fa[y];!isrt(x);rotate(x),y=fa[x],z=fa[y])
    if(!isrt(y))
      ((y==c[z][0])^(x==c[y][0]))?rotate(x):rotate(y);
}
void access(int x)
{
  splay(x); if(dfn[x]==tim)return;
  int t=0;
  while(1)
    {
      c[x][1]=t;
      if(!fa[x])
    { tg[x]++;vl[x]++;dfn[x]=tim;return;}
      splay(fa[x]);//splay first
      if(dfn[fa[x]]==tim)
    { tg[x]++;vl[x]++;dfn[x]=tim;return;}
      t=x; x=fa[x];
    }
}
void link(int x,int y){ fa[y]=x;}
int Ins()
{
  int cr=1,len=strlen(s+1);
  for(int i=1;i<=len;i++)
    {
      int w=s[i]-‘a‘;
      if(!tc[cr][w])tc[cr][w]=++tot;
      cr=tc[cr][w];
    }
  return cr;
}
void get_fl()
{
  int he=0,tl=0;
  for(int i=0,v;i<K;i++)
    if((v=tc[1][i]))
      {q[++tl]=v;fl[v]=1;link(1,v);}
    else tc[1][i]=1;
  while(he<tl)
    {
      int k=q[++he],pr=fl[k];
      for(int i=0,v;i<K;i++)
    if((v=tc[k][i]))
      { q[++tl]=v;fl[v]=tc[pr][i];link(tc[pr][i],v);}
    else tc[k][i]=tc[pr][i];
    }
}
void solve()
{
  tim++; int cr=1,len=strlen(s+1);
  for(int i=1;i<=len;i++)
    {
      cr=tc[cr][s[i]-‘a‘];
      access(cr);
    }
}
int main()
{
  n=rdn();
  for(int i=1;i<=n;i++)
    { scanf("%s",s+1); ps[i]=Ins();}
  get_fl();
  int Q=rdn(),op,x;
  while(Q--)
    {
      op=rdn();
      if(op==1)
    { scanf("%s",s+1); solve();}
      else
    {
      x=rdn(); x=ps[x];
      splay(x); printf("%d\n",vl[x]);
    }
    }
  return 0;
}

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

时间: 2024-10-09 20:09:52

bzoj 3881 [Coci2015]Divljak——LCT维护parent树链并的相关文章

BZOJ 3881 Coci2015 Divljak fail树+树链的并

题目大意:给定两个字符串集合S和T,初始给定S集合中的所有字符串,不断向T集合中添加字符串,以及询问S集合中的某个字符串在T集合中的多少个字符串中出现过 神题- - 首先对S集合的所有字符串构建fail树 T集合中每加入一个字符串,我们就将这个字符串在AC自动机上跑一遍,并记录经过的所有节点 根据fail树的性质,这些节点到根路径上的所有节点的并集的出现次数都加了1 因此我们要求的就是树链的并 求法如下: 将所有节点按照DFS序排序 每个点到根的路径上的所有节点权值+1 相邻两个点的LCA到根的

BZOJ 3881: [Coci2015]Divljak

Description 有两个集合\(ST\),\(S\)集合已知.有两个操作添加一个字符串到\(T\)询问T中有多少\(S_i\) \(n,q\leqslant 10^5,len(|S|),len(|T|)\leqslant 2\times 10^5\) Solution Trie树+DFS序. 添加一个字符串就要把Trie树上经过的节点及其fail树上的祖先+1将他差分一下,每次询问出现次数就变成了询问子树和.然后就是几条树梿的+1操作,因为差分过了,所以每个节点+1,相邻LCA-1 Cod

【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)

第一种做法(时间太感人): 这题我真的逗了,调了一下午,疯狂造数据,始终找不到错. 后来发现自己sb了,更新那里没有打id,直接套上u了.我.... 调了一下午啊!一下午的时光啊!本来说好中午A掉去学习第二种做法,噗 好吧,现在第一种做法是hld+seg+bst+二分,常数巨大,log^4级别,目前只会这种. 树剖后仍然用线段树维护dfs序区间,然后在每个区间建一颗平衡树,我用treap,(这题找最大啊,,,囧,并且要注意,这里的rank是比他大的数量,so,我们在二分时判断要判断一个范围,即要

bzoj 3626 [LNOI2014]LCA(离线处理+树链剖分,线段树)

3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1272  Solved: 451[Submit][Status][Discuss] Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1. 设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先. 有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[

bzoj 3637: Query on a tree VI 树链剖分 &amp;&amp; AC600

3637: Query on a tree VI Time Limit: 8 Sec  Memory Limit: 1024 MBSubmit: 206  Solved: 38[Submit][Status][Discuss] Description You are given a tree (an acyclic undirected connected graph) with n nodes. The tree nodes are numbered from 1 to n. Each nod

BZOJ 3631 JLOI2014 松鼠的新家 树链剖分/LCA

题目大意:给定一棵无根树和一个序列,在这个序列上依次遍历,求每个点的访问次数(最后一个点的访问次数要-1) 树链剖分的裸题--考场上我还是一个弱渣,啥也不会,暴力得了50分,剩下两道题爆零了...而且30W深搜爆栈,人生第一次手写了系统栈.. 回来因为这题的原因去学了树链剖分 结果没学明白 每条重链单独开了一棵线段树 常数大的要死 高一时写的代码...还是别看了,拿去对拍可以,阅读性欠佳 #include<stdio.h> #include<stdlib.h> #include&l

BZOJ 3631: [JLOI2014]松鼠的新家( 树链剖分 )

裸树链剖分... ------------------------------------------------------------------- #include<bits/stdc++.h> using namespace std; const int maxn = 300009; struct edge { int to; edge* next; } E[maxn << 1], *pit = E, *head[maxn]; inline void add(int u,

bzoj 1576: [Usaco2009 Jan]安全路经Travel 树链剖分

1576: [Usaco2009 Jan]安全路经Travel Time Limit: 10 Sec  Memory Limit: 64 MB Submit: 665  Solved: 227[Submit][Status] Description Input * 第一行: 两个空格分开的数, N和M * 第2..M+1行: 三个空格分开的数a_i, b_i,和t_i Output * 第1..N-1行: 第i行包含一个数:从牛棚_1到牛棚_i+1并且避免从牛棚1到牛棚i+1最短路经上最后一条牛

bzoj [Usaco2010 Hol]cowpol 奶牛政坛【树链剖分】

意识流虚树 首先考虑只有一个党派,那么可以O(n)求树的直径,步骤是随便指定一个根然后找距离根最远点,然后再找距离这个最远点最远的点,那么最远点和距离这个最远点最远的点之间的距离就是直径 那么考虑多党派,也这样做,假如有一棵只有这个党派的牛构成的虚树,那么求直径也可以按照上面的做法 但是实际上并不用虚树,直接在这个党派的牛中1.随便选一个牛然后找到距离它最远的本党派牛w 2.找到距离牛w最远的本党派牛,这之间的距离就是答案 求树上距离用deep相减(树剖求lca #include<iostrea