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

Code

/**************************************************************
    Problem: 3881
    User: BeiYu
    Language: C++
    Result: Accepted
    Time:16216 ms
    Memory:514652 kb
****************************************************************/

#include <bits/stdc++.h>
using namespace std;

const int N = 2000050;
const int M = 26;

namespace Bit {
    int d[N],n;

    void init(int x) { n=x; }
    void Add(int x,int v) { for(;x<=n;x+=x&-x) d[x]+=v; }
    int Qur(int x,int r=0) { for(;x;x-=x&-x) r+=d[x];return r; }
}

namespace Tree {
    vector<int> g[N];
    int cnt;
    int b[N],f[N][M],d[N],t1[N],t2[N];

    void AddEdge(int u,int v) { g[u].push_back(v),g[v].push_back(u); }
    void DFS(int rt) {
        stack<int> stk;
        stk.push(rt),f[rt][0]=rt,d[rt]=1;
        for(int x;!stk.empty();) {
            x=stk.top();
            if(b[x]) { t2[x]=cnt,stk.pop();continue; }
            b[x]=1,t1[x]=++cnt;
            for(int i=0,v;i<(int)g[x].size();i++)
                if((v=g[x][i])!=f[x][0]) f[v][0]=x,d[v]=d[x]+1,stk.push(v);
        }
    }
    void init() {
        for(int j=1;j<M;j++) for(int i=1;i<=cnt;i++)
            f[i][j]=f[f[i][j-1]][j-1];
    }
    int LCA(int u,int v) {
        if(d[u]<d[v]) swap(u,v);
        int l=d[u]-d[v];
        if(l) for(int i=M-1;~i;i--) if(l&(1<<i)) u=f[u][i];
        if(u==v) return u;
        for(int i=M-1;~i;--i) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
        return f[u][0];
    }
};

namespace AC {
    int cnt,rt;
    int f[N],ch[N][M],ed[N];

    void init() { rt=cnt=0; }
    void insert(char* s,int id) {
        int l=strlen(s),x=rt;
        for(int i=0;i<l;i++) {
            int v=s[i]-‘a‘;
            if(!ch[x][v]) ch[x][v]=++cnt;
            x=ch[x][v];
        }ed[id]=x;
    }
    void Build() {
        queue<int> q;
        f[rt]=rt;
        for(int i=0;i<M;i++) if(ch[rt][i]) q.push(ch[rt][i]),f[ch[rt][i]]=rt;
        for(int x;!q.empty();) {
            x=q.front(),q.pop();
            for(int i=0;i<M;i++) {
                if(!ch[x][i]) { ch[x][i]=ch[f[x]][i];continue; }
                int v=ch[x][i];
                f[v]=ch[f[x]][i],q.push(v);
            }
        }
    }
    void get_f() {
        for(int i=1;i<=cnt;i++) Tree::AddEdge(i,f[i]);
        Tree::DFS(0);
        Tree::init();

//      for(int i=1;i<=cnt;i++) cout<<i<<" "<<f[i]<<endl;
//      for(int i=1;i<=cnt;i++) cout<<Tree::d[i]<<" ";cout<<endl;
//      for(int i=1;i<=cnt;i++) cout<<Tree::t1[i]<<" ";cout<<endl;
//      for(int i=1;i<=cnt;i++) cout<<Tree::t2[i]<<" ";cout<<endl;
    }
};

int cmp(int x,int y) { return Tree::t1[x]<Tree::t1[y]; }
vector<int> a;
void add_str(char* s) {
    int l=strlen(s),x=AC::rt;
    a.clear();
    for(int i=0;i<l;i++) {
        int v=s[i]-‘a‘;
        x=AC::ch[x][v];
        a.push_back(x);
    }
    sort(a.begin(),a.end(),cmp);
    x=a.size();
//  for(int i=0;i<x;i++) cout<<a[i]<<" ";cout<<endl;
    Bit::Add(Tree::t1[a[0]],1);
    for(int i=1;i<x;i++) {
        Bit::Add(Tree::t1[Tree::LCA(a[i],a[i-1])],-1);
        Bit::Add(Tree::t1[a[i]],1);
    }
}

int n,q;
char s[N];

int main() {
//  freopen("in.in","r",stdin);
    scanf("%d",&n);
    AC::init();
    for(int i=1;i<=n;i++) {
        scanf("%s",s);
        AC::insert(s,i);
    }
    AC::Build();
    AC::get_f();
    Bit::init(AC::cnt+1);
//  int oo,ooo;
//  while(cin>>oo>>ooo) cout<<Tree::LCA(oo,ooo)<<endl;
    scanf("%d",&q);
    for(int opt,x;q--;) {
        scanf("%d",&opt);
        if(opt==1) {
            scanf("%s",s);
            add_str(s);
        } else {
            scanf("%d",&x);
            printf("%d\n",Bit::Qur(Tree::t2[AC::ed[x]])-Bit::Qur(Tree::t1[AC::ed[x]]-1));
        }
    }return 0;
}

  

时间: 2024-10-13 01:03:26

BZOJ 3881: [Coci2015]Divljak的相关文章

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 fail树+树链的并

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

bzoj[3881]Divljak(dfs序+树状数组+fail树)

这道题利用了fail树的神奇性质————父节点为其子节点的前缀 先对Alice的集合建一个fail树, Bob每插入一个串,都将串在自动机上经过的点在树上打上标记(+1) 每次查询的答案就是询问串的结束节点的子树的贡献 所以还需要用到树状数组来维护dfs序 因为Bob的一个串至多只能对Alice的某一个串做出1的贡献,所以不能单纯加和 要对树链取并 可以将一个Bob串在fail树上经过的所有点按入栈顺序排序,相邻每两个点的lca贡献-1 可以证明,这样会使每次某个点的子树内没有重复贡献 1 #i

bzoj3881 [Coci2015]Divljak

传送门 我好菜啊--这种题都不会做--lrd给我讲了之后我才会的-- 不难想到对所有询问串建AC自动机,然后可以在每次加进来一个文本串时维护一下答案,询问的时候直接回答. 每次扔进来一个文本串的时候fail树上经过的节点到根节点的所有节点的出现次数都会+1,但注意有很多节点会被算重了,这时就需要搞一个树链的并. 首先把节点按fail树的dfs序排序,每个点的标记+1,相邻两个节点的LCA处标记-1.不难看出来这样刚好可以不重不漏覆盖树链的并的所有点,这样询问时直接子树求和即可.因为需要动态维护区

bzoj 3745: [Coci2015]Norma

Description 给定序列\(a_i\) 求 \[\sum_{i=1}^n \sum_{j=i}^n (j-i+1)\max\{a_i,a_{i+1}\cdots a_j\}\min\{a_i,a_{i+1}\cdots a_j\}\] Input 第1行,一个整数N: 第2~n+1行,每行一个整数表示序列a. Output 输出答案对10^9取模后的结果. Sample Input 4 2 4 1 4 Sample Output 109 [数据范围] \(N \le 500000\) \

bzoj 3745 [Coci2015]Norma——序列分治

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3745 如果分治,就能在本层仅算过 mid 的区间了. 可以从中间到左边地遍历左边,给右边两个指针,表示第一个更新左边造成的最小值/最大值的位置. 两个位置共同的左边可以公式算长度,用左边的最值算:两个位置共同的右边可以预处理,处理出 算上长度(相对mid的)的最值乘积求和 与 不算长度的最值乘积求和(都是前缀),把前者加到答案里,后者乘上左边到mid的长度加到答案里即可:两个位置夹着的位置

bzoj 3745: [Coci2015]Norma【分治】

参考:https://blog.csdn.net/lych_cys/article/details/51203960 真的不擅长这种-- 分治,对于一个(l,r),先递归求出(l,mid),(mid+1,r),然后这个区间对答案贡献的就是经过mid的区间 我们先预处理出mid为l的右端点的mx*mn*len的前缀和与mx*mn的前缀和,然后枚举左端点,右端点维护两个下标j,k,分别表示mn和mx在左端点时的合法右端点 然后分三种情况处理,假设j<k 1.右端点在(mid+1,j)时,直接计算 2

[Coci2015]Divljak

题目大意: Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: "1 P",Bob往自己的集合里添加了一个字符串P. "2 x",Alice询问Bob,集合T中有多少个字符串包含串S_x.(我们称串A包含串B,当且仅当B是A的子串) Bob遇到了困难,需要你的帮助. 题解: 建出fail树然后搞树链的并-- (恶心) 正经的题解开始. 先建出关于S的trie树,把fail树建出来.

P5840 [COCI2015]Divljak

// powered by c++11 // by Isaunoya #include <bits/stdc++.h> #define rep(i, x, y) for (register int i = (x); i <= (y); ++i) #define Rep(i, x, y) for (register int i = (x); i >= (y); --i) using namespace std; using db = double; using ll = long l