传送门


一道很套路的题目

先将所有串拼在一起,两个不同的串之间放一个没有出现在任何串中的字符做分隔,然后SA

那么对于所有点名串能够点到的名字串在SA中对应一段区间

把这些区间拿出来然后莫队统计每一个区间的答案

如何在莫队中统计一个名字在多少个点名串中出现?

当某一个名字第一次出现在区间内的时候,答案加上剩余询问个数;当这个名字第一次不出现在区间内的时候,答案减去剩余询问个数。

#include<bits/stdc++.h>
//This code is written by Itst
using namespace std;

inline int read(){
    int a = 0;
    char c = getchar();
    bool f = 0;
    while(!isdigit(c) && c != EOF){
        if(c == '-')
            f = 1;
        c = getchar();
    }
    if(c == EOF)
        exit(0);
    while(isdigit(c)){
        a = a * 10 + c - 48;
        c = getchar();
    }
    return f ? -a : a;
}

const int MAXN = 5e5 + 9;
int s[MAXN] , rk[MAXN << 1] , pot[MAXN] , tp[MAXN << 1] , sa[MAXN] , h[MAXN];
int N , M , L , maxN = 10001 , T , logg2[MAXN] , ST[21][MAXN];
int lft , cnt , ans1[MAXN] , ans2[MAXN] , times[MAXN] , ind[MAXN];
struct query{
    int l , r , ind;
    bool operator <(const query a)const{
        return l / T == a.l / T ? ((l / T) & 1 ? r > a.r : r < a.r) : l < a.l;
    }
}now[MAXN];

void sort(int p){
    memset(pot , 0 , sizeof(int) * (maxN + 1));
    for(int i = 1 ; i <= L ; ++i)
        ++pot[rk[i]];
    for(int i = 1 ; i <= maxN ; ++i)
        pot[i] += pot[i - 1];
    for(int i = 1 ; i <= L ; ++i)
        sa[++pot[rk[tp[i]] - 1]] = tp[i];
    swap(rk , tp);
    for(int i = 1 ; i <= L ; ++i)
        rk[sa[i]] = rk[sa[i - 1]] + (tp[sa[i]] != tp[sa[i - 1]] || tp[sa[i] + p] != tp[sa[i - 1] + p]);
    maxN = rk[sa[L]];
}

void SA(){
    for(int i = 1 ; i <= L ; ++i){
        rk[i] = s[i];
        tp[i] = i;
    }
    sort(0);
    for(int i = 1 ; maxN < L ; i <<= 1){
        int cnt = 0;
        for(int j = 1 ; j <= i ; ++j)
            tp[++cnt] = L - i + j;
        for(int j = 1 ; j <= L ; ++j)
            if(sa[j] > i)
                tp[++cnt] = sa[j] - i;
        sort(i);
    }
    for(int i = 1 ; i <= L ; ++i){
        if(rk[i] == 1)
            continue;
        int t = rk[i];
        h[t] = max(0 , h[rk[i - 1]] - 1);
        while(s[h[t] + sa[t]] == s[h[t] + sa[t - 1]])
            ++h[t];
    }
}

inline void init_SA(){
    N = read();
    M = read();
    for(int i = 1 ; i <= N ; ++i){
        int l = read();
        for(int j = 1 ; j <= l ; ++j){
            s[++L] = read() + 1;
            ind[L] = i;
        }
        s[++L] = ++maxN;
        l = read();
        for(int j = 1 ; j <= l ; ++j){
            s[++L] = read() + 1;
            ind[L] = i;
        }
        s[++L] = maxN;
    }
    for(int i = 1 ; i <= M ; ++i){
        int l = read();
        for(int j = 1 ; j <= l ; ++j){
            s[++L] = read() + 1;
            ind[L] = j == 1 ? -l : 0;
        }
        s[++L] = ++maxN;
    }
    SA();
}

inline void init_ST(){
    for(int i = 2 ; i <= L ; ++i){
        logg2[i] = logg2[i >> 1] + 1;
        ST[0][i] = h[i];
    }
    for(int i = 1 ; 1 << i <= L ; ++i)
        for(int j = 2 ; j + (1 << i) <= L + 1 ; ++j)
            ST[i][j] = min(ST[i - 1][j] , ST[i - 1][j + (1 << (i - 1))]);
}

inline int qST(int x , int y){
    int t = logg2[y - x + 1];
    return min(ST[t][x] , ST[t][y - (1 << t) + 1]);
}

inline void GetQue(int dir){
    T = sqrt(L);
    ++M;
    now[M].ind = M;
    int L = 1 , R = rk[dir];
    while(L < R){
        int mid = (L + R) >> 1;
        if(qST(mid + 1 , rk[dir]) >= -ind[dir])
            R = mid;
        else
            L = mid + 1;
    }
    now[M].l = L;
    L = rk[dir];
    R = L;
    while(L < R){
        int mid = (L + R + 1) >> 1;
        if(qST(rk[dir] + 1 , mid) >= -ind[dir])
            L = mid;
        else
            R = mid - 1;
    }
    now[M].r = L;
}   

inline void init_que(){
    init_ST();
    M = 0;
    for(int i = 1 ; i <= L ; ++i)
        if(ind[i] < 0)
            GetQue(i);
    sort(now + 1 , now + M + 1);
}

inline void add(int x){
    if(x <= 0)
        return;
    if(!times[x]++){
        ans2[x] += lft;
        ++cnt;
    }
}

inline void del(int x){
    if(x <= 0)
        return;
    if(!--times[x]){
        ans2[x] -= lft;
        --cnt;
    }
}

inline void work(){
    lft = M;
    int L = 1 , R = 0;
    for(int i = 1 ; i <= M ; ++i , --lft){
        while(R < now[i].r)
            add(ind[sa[++R]]);
        while(L > now[i].l)
            add(ind[sa[--L]]);
        while(R > now[i].r)
            del(ind[sa[R--]]);
        while(L < now[i].l)
            del(ind[sa[L++]]);
        ans1[now[i].ind] = cnt;
    }
}

inline void output(){
    for(int i = 1 ; i <= M ; ++i)
        cout << ans1[i] << '\n';
    for(int i = 1 ; i <= N ; ++i)
        cout << ans2[i] << ' ';
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("in","r",stdin);
    //freopen("out","w",stdout);
#endif
    init_SA();
    init_que();
    work();
    output();
    return 0;
}

Luogu2336 SCOI2012 喵星球上的点名 SA、莫队的更多相关文章

  1. BZOJ2754 [SCOI2012]喵星球上的点名 SA+莫队+树状数组

    题面 戳这里 题解 首先先把所有给出的姓名和询问全部接在一起,建出\(height\)数组. 某个串要包含整个询问串,其实就相当于某个串与询问串的\(lcp\)为询问串的长度. 而两个后缀\(Suff ...

  2. BZOJ2754: [SCOI2012]喵星球上的点名

    2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 680  Solved: 314[Submit][Sta ...

  3. BZOJ 2754: [SCOI2012]喵星球上的点名 [后缀数组+暴力]

    2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1906  Solved: 839[Submit][St ...

  4. 洛咕 P2336 [SCOI2012]喵星球上的点名

    洛咕 P2336 [SCOI2012]喵星球上的点名 先求出SA和height,一个点名串对应的就是一段区间,还有很多个点,就转化成了 有很多个区间,很多个点集,对每个区间计算和多少个点集有交,对每个 ...

  5. 【BZOJ2754】[SCOI2012]喵星球上的点名

    [BZOJ2754][SCOI2012]喵星球上的点名 题面 bzoj 洛谷 题解 这题有各种神仙做法啊,什么暴力\(AC\)自动机.\(SAM\)等等五花八门 我这个蒟蒻在这里提供一种复杂度正确且常 ...

  6. BZOJ 2754: [SCOI2012]喵星球上的点名

    2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 649  Solved: 305[Submit][Sta ...

  7. BZOJ 2754: [SCOI2012]喵星球上的点名 [AC自动机+map+暴力]

    2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1902  Solved: 837[Submit][St ...

  8. P2336 [SCOI2012]喵星球上的点名(后缀自动机+莫队+dfs序)

    P2336 [SCOI2012]喵星球上的点名 名字怎么存?显然是后缀自动机辣 询问点到多少个喵喵喵其实就是 查询后缀自动机上parent树的一个子树 于是我们考虑莫队 怎么树上莫队呢 我们用dfs序 ...

  9. 洛谷 P2336 [SCOI2012]喵星球上的点名 解题报告

    P2336 [SCOI2012]喵星球上的点名 题目描述 a180285 幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣. 假设课堂上有 \(N\) 个喵星人,每个喵星人的 ...

随机推荐

  1. Apex单元测试

    单元测试类 Salesforce中为Apex语言提供了完整的单元测试流程,包括单元测试类.测试的运行和结果分析等. 单元测试类是一种特殊的Apex类,基本语法和普通的Apex类一样. 单元测试类的结构 ...

  2. jenkins离线插件安装--笨方法

    Jenkins离线安装插件有多种方式:代理or离线导入,但离线导入可能会存在版本差异或依赖的插件文件导致异常发生), 以下为笨方法但会很准确的解决以上的问题. 同版本Jenkins在线下载:模糊掉的是 ...

  3. 第一章 Hyper-V 2012 R2角色部署

      在windows server 2012 R2中,我们可以通过安装hyper-v角色来完成虚拟化底层架构的部署.除了图形界面的安装,也可以使用单独的发行版Hyper-V Server 2012 R ...

  4. 用Python实现数据结构之优先级队列

    优先级队列 如果我们给每个元素都分配一个数字来标记其优先级,不妨设较小的数字具有较高的优先级,这样我们就可以在一个集合中访问优先级最高的元素并对其进行查找和删除操作了.这样,我们就引入了优先级队列 这 ...

  5. 禁用selinux

    查看selinux状态: [root@VM000000518 upload]# getenforce Enforcing 禁用: [root@VM000000518 upload]# setenfor ...

  6. 【PAT】B1084 外观数列(20 分)(纯C)

    第一层循环,用来循环计算第几个元素 第二层用来计算当前元素的下一个 #include<stdio.h> #include<string.h> char aaa[100000] ...

  7. Elasticsearch一些常用操作和一些基础概念

    1.查看集群健康状态 [root@ELK-chaofeng01 ~]#curl -XGET http://172.16.0.51:9200/_cat/health?v epoch timestamp ...

  8. 【转】ICCAVR TAB键设置

    转载于: http://blog.163.com/liuyunqian@yeah/blog/static/7039584320099159545292/ 在使用ICCAVR C编译器的时候会发现TAB ...

  9. Unity3D中自带事件函数的执行顺序

    在Unity3D脚本中,有几个Unity3D自带的事件函数按照预定的顺序执行作为脚本执行.其执行顺序如下: 编辑器(Editor) Reset:Reset函数被调用来初始化脚本属性当脚本第一次被附到对 ...

  10. 跨域访问的解决方案(非HTML5的方法:JSONP)

    http://supercharles888.blog.51cto.com/609344/856886 跨域访问一直是困扰很多开发者的问题之一.因为涉及到安全性问题,所以跨域访问默认是不可以进行的,否 ...