BZOJ 3881 Coci2015 Divljak fail树+树链的并

题目大意:给定两个字符串集合S和T,初始给定S集合中的所有字符串,不断向T集合中添加字符串,以及询问S集合中的某个字符串在T集合中的多少个字符串中出现过

神题- -

首先对S集合的所有字符串构建fail树

T集合中每加入一个字符串,我们就将这个字符串在AC自动机上跑一遍,并记录经过的所有节点

根据fail树的性质,这些节点到根路径上的所有节点的并集的出现次数都加了1

因此我们要求的就是树链的并

求法如下:

将所有节点按照DFS序排序

每个点到根的路径上的所有节点权值+1

相邻两个点的LCA到根的路径上的所有节点权值-1

即是树链的并

当然我们不必修改路径查询单点 只需对单点进行修改 然后查询子树 用树状数组维护DFS序即可

这代码长度。。。。为了防止内存爆炸我用了线段树+RMQLCA。。。namespace套namespace泥萌怕不怕-。-

暴力艹标程是什么节奏0 0 快卡掉他们- -

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 2002002
using namespace std;
int n,m,limit;
char s[M];
namespace BIT{
    int c[M];
    void Update(int x,int y)
    {
        for(;x<=limit;x+=x&-x)
            c[x]+=y;
    }
    int Get_Ans(int x)
    {
        int re=0;
        for(;x;x-=x&-x)
            re+=c[x];
        return re;
    }
}
namespace Fail_Tree{
    struct abcd{
        int to,next;
    }table[M];
    int head[M],tot;
    int fa[M],dpt[M],pos[M],into[M],ed[M];
    namespace ZKW_Segtree{
        int Q,tree[8389000];
        bool Compare(int x,int y)
        {
            if(!x) return false;
            if(!y) return true;
            return dpt[x]<dpt[y];
        }
        void Build_Tree()
        {
            int i;
            for(i=Q-1;i;i--)
                tree[i]=min(tree[i<<1],tree[i<<1|1],Compare);
        }
        int Query(int x,int y)
        {
            int re=0;
            for(x+=Q-1,y+=Q+1;x^y^1;x>>=1,y>>=1)
            {
                if(~x&1) re=min(re,tree[x^1],Compare);
                if( y&1) re=min(re,tree[y^1],Compare);
            }
            return re;
        }
    }
    void DFS(int x,int array[])
    {
        static int cnt1,cnt2;
        int i;
        dpt[x]=dpt[fa[x]]+1;
        pos[x]=++cnt1;
        array[into[x]=++cnt2]=x;
        for(i=head[x];i;i=table[i].next)
        {
            fa[table[i].to]=x;
            DFS(table[i].to,array);
            array[++cnt2]=x;
        }
        ed[x]=cnt1;
    }
    void Add(int x,int y)
    {
        table[++tot].to=y;
        table[tot].next=head[x];
        head[x]=tot;
    }
    void Build_Tree()
    {
        using namespace ZKW_Segtree;
        for(Q=1;Q<=limit+limit;Q<<=1);
        DFS(1,tree+Q);
        ZKW_Segtree::Build_Tree();
    }
    int LCA(int x,int y)
    {
        x=into[x];y=into[y];
        if(x>y) swap(x,y);
        return ZKW_Segtree::Query(x,y);
    }
}
namespace Aho_Corasick_Automaton{
    struct Trie{
        Trie *son[26],*fail;
    }*root,mempool[M],*C=mempool;
    Trie *pos[100100];
    void Insert(Trie *&p,char *s,int id)
    {
        if(!p) p=++C;
        if(!*s)
        {
            pos[id]=p;
            return ;
        }
        Insert(p->son[*s-'a'],s+1,id);
    }
    void Build_Tree()
    {
        static Trie *q[M];
        int i,r=0,h=0;
        for(i=0;i<26;i++)
        {
            if(root->son[i])
                (q[++r]=root->son[i])->fail=root;
            else
                root->son[i]=root;
        }
        while(r!=h)
        {
            Trie *p=q[++h];
            for(i=0;i<26;i++)
            {
                if(p->son[i])
                    (q[++r]=p->son[i])->fail=p->fail->son[i];
                else
                    p->son[i]=p->fail->son[i];
            }
        }
        for(i=2;i<=C-mempool;i++)
            Fail_Tree::Add(mempool[i].fail-mempool,i);
        limit=C-mempool;
    }
    int Get_Points(char *s,int a[])
    {
        int i;
        Trie *p=root;
        for(i=1;s[i];i++)
        {
            p=p->son[s[i]-'a'];
            a[i]=p-mempool;
        }
        return a[i]=-1,i-1;
    }
}
bool Compare(int x,int y)
{
    using namespace Fail_Tree;
    return pos[x]<pos[y];
}
int main()
{
    int i,j,p,x;
    cin>>n;
    for(i=1;i<=n;i++)
    {
        scanf("%s",s+1);
        Aho_Corasick_Automaton::Insert(Aho_Corasick_Automaton::root,s+1,i);
    }
    Aho_Corasick_Automaton::Build_Tree();
    Fail_Tree::Build_Tree();
    static int a[M],top;
    cin>>m;
    using namespace Fail_Tree;
    for(i=1;i<=m;i++)
    {
        scanf("%d",&p);
        if(p==1)
        {
            scanf("%s",s+1);
            x=Aho_Corasick_Automaton::Get_Points(s,a);
            sort(a+1,a+x+1,Compare);
            for(top=0,j=1;j<=x;j++)
                if(a[j]!=a[j+1])
                    a[++top]=a[j];
            for(j=1;j<=top;j++)
            {
                x=a[j];
                BIT::Update(pos[x],1);
                if(j>1)
                    BIT::Update(pos[LCA(a[j-1],x)],-1);
            }
        }
        else
        {
            scanf("%d",&x);
            p=Aho_Corasick_Automaton::pos[x]-Aho_Corasick_Automaton::mempool;
            printf("%d\n",BIT::Get_Ans(ed[p])-BIT::Get_Ans(pos[p]-1) );
        }
    }
    return 0;
}
时间: 2024-10-05 08:41:31

BZOJ 3881 Coci2015 Divljak fail树+树链的并的相关文章

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]) . #includ

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

acm 2015北京网络赛 F Couple Trees 主席树+树链剖分

提交 题意:给了两棵树,他们的跟都是1,然后询问,u,v 表 示在第一棵树上在u点往根节点走 , 第二棵树在v点往根节点走,然后求他们能到达的最早的那个共同的点 解: 我们将第一棵树进行书链剖,然后第二棵树采用主席树,他的信息来自他的父亲节点,每个点存他在第一棵树 树链剖分后的位置,这样我们每次查询uv的时候我们只要 我们选取u和top[u]这段区间在主席树v这颗树上找,在这个区间能取到的最大值,一旦存在,这个最大值就我们要的,这个点保存着他到根节点这条路上所有点在第一棵树剖分后的位置 #inc

BZOJ 1835 基站选址(线段树优化DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1835 题意:有N个村庄坐落在一条直线上,第 i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村 庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了.如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi.现在的问题是,选择基站的位 置,使得总费用最小. 思路: 另外,程序中的n=n+1,m=

BZOJ 3211 花神游历各国 线段树题解

BZOJ 3211 花神游历各国 线段树题解 3211: 花神游历各国 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 2551  Solved: 946[Submit][Status][Discuss] Description Input Output 每次x=1时,每行一个整数,表示这次旅行的开心度 Sample Input 4 1 100 5 5 5 1 1 2 2 1 2 1 1 2 2 2 3 1 1 4 Sample Output 101

数据结构 树的链式存储(二叉表示法)

//树的链式存储--二叉表示法 #include<stdio.h> #include<stdlib.h> #include<string.h> typedef struct _TreeNode{ //数据域 int data; //指针域 struct _TreeNode * leftchild;//左孩子指针 struct _TreeNode * rightchild;//右孩子指针 }TreeNode, *TreeNodePointer; /* 以上语法定义了两个类

数据结构 树的链式存储(双亲表示法)

//树的链式存储--双亲表示法 #include<stdio.h> #include<stdlib.h> #include<string.h> #define MAX_TREE_SIZE 100 typedef struct BPTNode { char data;//数据域 int parentPosition; //双亲的数组下标 char LRTag; //左右孩子标志域 }BPTNode; typedef struct BPTree { BPTNode node

数据结构 树的链式存储(三叉表示法)

//树的链式存储--三叉表示法 #include<stdio.h> #include<stdlib.h> #include<string.h> typedef struct _TreeNode{ //数据域 int data; //指针域 struct _TreeNode * leftchild;//左孩子指针 struct _TreeNode * rightchild;//右孩子指针 struct _TreeNode * parent;//双亲指针---比二叉表示法多

BZOJ 3211 花神游历各国 (树状数组+并查集)

题解:首先,单点修改求区间和可以用树状数组实现,因为开平方很耗时间,所以在这个方面可以优化,我们知道,开平方开几次之后数字就会等于1 ,所以,用数组记录下一个应该开的数,每次直接跳到下一个不是1的数字进行开平方,至于这个数组,可以用并查集维护. #include <cstdio> #include <cmath> #include <iostream> using namespace std; typedef long long LL; LL c[100005]; in