bzoj 5408: string 后缀自动机+动态树

联赛前练练码力.

code:

#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200006
#define ll long long
#define lson t[x].ch[0]
#define rson t[x].ch[1]
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
struct data
{
    int tim,id;
    data(int tim=0,int id=0):tim(tim),id(id){}
};
struct node
{
    int ch[2],f,val[21],tag[21];
}t[N<<1];
ll sub;
int lastans;
char str[N];
int last,tot,n;
vector<data>G[21];
int ch[N<<1][11],pre[N<<1],len[N<<1],sta[N<<1];
inline int get(int x)
{
    return t[t[x].f].ch[1]==x;
}
inline int isrt(int x)
{
    return !(t[t[x].f].ch[0]==x||t[t[x].f].ch[1]==x);
}
inline void mark(int x,int p,ll v)
{
    t[x].val[p]+=v,t[x].tag[p]+=v;
}
void pushdown(int x)
{
    if(!x) return;
    for(int i=1;i<=n;++i)
    {
        if(t[x].tag[i])
        {
            if(lson)   mark(lson,i,t[x].tag[i]);
            if(rson)   mark(rson,i,t[x].tag[i]);
            t[x].tag[i]=0;
        }
    }
}
void rotate(int x)
{
    int old=t[x].f,fold=t[old].f,which=get(x);
    if(!isrt(old))    t[fold].ch[t[fold].ch[1]==old]=x;
    t[old].ch[which]=t[x].ch[which^1],t[t[old].ch[which]].f=old;
    t[x].ch[which^1]=old,t[old].f=x,t[x].f=fold;
}
void splay(int x)
{
    int u=x,fa,v=0;
    for(sta[++v]=u;!isrt(u);u=t[u].f)    sta[++v]=t[u].f;
    for(;v;--v)    pushdown(sta[v]);
    for(u=t[u].f;(fa=t[x].f)!=u;rotate(x))
        if(t[fa].f!=u)     rotate(get(fa)==get(x)?fa:x);
}
void Access(int x)
{
    for(int y=0;x;y=x,x=t[x].f)
    {
        splay(x);
        rson=y;
    }
}
// y 无父亲
void link(int x,int y)
{
    t[y].f=x;
}
// x 是 y 的父亲
void cut(int x,int y)
{
    Access(y),splay(y);
    t[t[y].ch[0]].f=0;
    t[y].ch[0]=0;
}
void extend(int id,int c)
{
    if(ch[last][c])
    {
        int p=last;
        int q=ch[p][c];
        if(len[q]==len[p]+1)   last=q;
        else
        {
            int nq=++tot;
            len[nq]=len[p]+1;
            memcpy(ch[nq],ch[q],sizeof(ch[q]));
            cut(pre[q],q);
            for(int j=1;j<=n;++j)  t[nq].val[j]=t[q].val[j];
            link(pre[q],nq);
            link(nq,q);
            pre[nq]=pre[q],pre[q]=nq;
            for(;p&&ch[p][c]==q;p=pre[p])    ch[p][c]=nq;
            last=nq;
        }
    }
    else
    {
        int np=++tot,p=last;
        len[np]=len[p]+1,last=np;
        for(;p&&!ch[p][c];p=pre[p])    ch[p][c]=np;
        if(!p)   pre[np]=1,link(1,np);
        else
        {
            int q=ch[p][c];
            if(len[q]==len[p]+1)   pre[np]=q,link(q,np);
            else
            {
                int nq=++tot;
                len[nq]=len[p]+1;
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                cut(pre[q],q);
                for(int j=1;j<=n;++j)  t[nq].val[j]=t[q].val[j];
                link(pre[q],nq);
                link(nq,q);
                link(nq,np);
                pre[nq]=pre[q],pre[q]=pre[np]=nq;
                for(;p&&ch[p][c]==q;p=pre[p])    ch[p][c]=nq;
            }
        }
        sub+=len[np]-len[pre[np]];
    }
    Access(last),splay(last),mark(last,id,1ll);
}
int main()
{
    // setIO("input");
    int i,j,ty,m;
    scanf("%d%d",&n,&ty);
    for(tot=i=1;i<=n;++i)
    {
        scanf("%s",str+1);
        int len=strlen(str+1);
        for(last=j=1;j<=len;++j)   extend(i,str[j]-‘0‘);
        G[i].push_back(data(0,last));
    }
    scanf("%d",&m);
    for(i=1;i<=m;++i)
    {
        int op,x,y,z;
        scanf("%d",&op);
        if(op==1)
        {
            scanf("%d%d",&x,&y);
            y=(y^(lastans*1ll*ty))%10;
            last=G[x][G[x].size()-1].id;
            extend(x,y);
            G[x].push_back(data(i,last));
        }
        if(op==2)
        {
            scanf("%d%d%d",&x,&y,&z);
            int l=0,r=G[x].size()-1,mid=0,pp=0;
            while(l<=r)
            {
                mid=(l+r)>>1;
                if(G[x][mid].tim<=y)    pp=G[x][mid].id,l=mid+1;
                else r=mid-1;
            }
            Access(pp),splay(pp);
            printf("%d\n",lastans=t[pp].val[z]);
        }
        if(op==3)
        {
            printf("%lld\n",sub);
        }
        if(op==4)
        {
            scanf("%s",str+1);
            int len=strlen(str+1),pp=1,flag=0;
            for(j=1;j<=len;++j)
            {
                if(ch[pp][str[j]-‘0‘])   pp=ch[pp][str[j]-‘0‘];
                else { flag=1;break; }
            }
            // 能匹配
            if(!flag)
            {
                Access(pp);
                int best=0;
                for(j=1;j<=n;++j)    best=max(best,t[pp].val[j]);
                lastans=best;
            }
            else lastans=0;
            printf("%d\n",lastans);
        }
    }
    return 0;
}

  

原文地址:https://www.cnblogs.com/guangheli/p/11829165.html

时间: 2024-08-28 16:01:16

bzoj 5408: string 后缀自动机+动态树的相关文章

识别子串 (string)——后缀自动机+线段树

题目 [题目描述] 一般地,对于一个字符串 S,和 S 中第 $ i $ 个字符 x,定义子串 $ T=S(i.j) $ 为一个关于 x 的识别子申,当且仅当: 1.$ i \leq x \leq j $ 2.T 在 S 巾只出现一次 比如,对于 banana 的第 $ 5 $ 个字符,“nana”, “anan”,“anana”, “nan”,“banan” 和“banana”都是关于它的识别子串. 说你写一个程序,计算出对对于一个字符串 S,关于 S 的每一位的最短识别子串的长度. [输入格

bzoj 3413: 匹配 后缀自动机+线段树合并

并不是很难啊,把细节想好了再写就很轻松了~ code: #include <bits/stdc++.h> #define N 200003 #define LL long long #define setIO(s) freopen(s".in","r",stdin) ,freopen(s".out","w",stdout) using namespace std; struct SAM { int tot,last

【hihocoder#1413】Rikka with String 后缀自动机 + 差分

题目链接:http://hihocoder.com/problemset/problem/1413 这个题非常的劲! 首先可以发现,每次只变换一个字符为#,所以每次答案一定会得到相应的包含#的答案,而这个方案是可以直接计算出来的. 假设是$S[i]=$#则会得到$i*(N-i+1)$的子串数. 所以每次的答案可以表示为$sum[root]+i*(N-i+1)-ans[i]$,其中$ans[i]$表示严格经过$i$位置的本质不同的子串,严格的意义即这个本质不同的子串有且仅有一次,且经过$i$: 所

[BZOJ1396]识别子串 后缀自动机+线段树

1396: 识别子串 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 451  Solved: 290[Submit][Status][Discuss] Description Input 一行,一个由小写字母组成的字符串S,长度不超过10^5 Output L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. Sample Input agoodcookcooksgoodfood Sample Output 1 2 3 3

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

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

CF666E Forensic Examination(后缀自动机+线段树合并)

给你一个串S以及一个字符串数组T[1..m],q次询问,每次问S的子串S[pl..pr]在T[l..r]中的哪个串里的出现次数最多,并输出出现次数. 如有多解输出最靠前的那一个. 我们首先对m个字符串数组建出后缀自动机,然后我们可以通过跳trans边找到S前i个字符代表的前缀的最长后缀.我们要找的是S[pl..pr]并不是以pr结束最长的后缀,但我们可以确定S[pl..pr]一定是当前点的祖先所以当我们跳到pr代表的点时我们倍增往上跳知道找到一个点的长度刚好大于等于pr-pl+1,这个点就是询问

HDU - 6704 K-th occurrence (后缀数组+主席树/后缀自动机+线段树合并+倍增)

题意:给你一个长度为n的字符串和m组询问,每组询问给出l,r,k,求s[l,r]的第k次出现的左端点. 解法一: 求出后缀数组,按照排名建主席树,对于每组询问二分或倍增找出主席树上所对应的的左右端点,求第k大的下标即可. 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=1e5+10,mod=998244353; 5 char buf[N]; 6 int s[N],sa[

BZOJ 2882: 工艺( 后缀自动机 )

把串S复制成SS然后扔进后缀自动机里, 从根选最小的儿子走, 走N步就是答案了...一开始还想写个treap的...后来觉得太麻烦..就用map了... --------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<map> using nam

BZOJ 1095 ZJOI2007 Hide 捉迷藏 动态树分治+堆

题目大意:给定一棵树,一开始每个点都是黑点,多次改变某个点的状态或询问距离最远的两个黑点的距离 <珍爱生命远离STL可是我还是可耻地用了STL系列> 传说中的动态树分治...其实并没有那么神嘛= = ↑别听这傻瓜瞎说这货被STL卡了一天QAQ 我们把分治过程中遍历过的重心都连起来 上一层的重心链接下一层的重心 可以得到一棵新的树 下面我们开始讨论这棵新树 显然这棵树的高度不会超过O(logn) 然后我们每个节点开两个堆 第一个堆记录子树中所有节点到父亲节点的距离 第二个堆记录所有子节点的堆顶