zoukankan      html  css  js  c++  java
  • BZOJ 3439: Kpm的MC密码( trie + DFS序 + 主席树 )

    把串倒过来插进trie上, 那么一个串的kpm串就是在以这个串最后一个为根的子树, 子树k大值的经典问题用dfs序+可持久化线段树就可以O(NlogN)解决

    ------------------------------------------------------------------

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cctype>
     
    using namespace std;
     
    #define c(x) ((x) - 'a')
    #define chk(x) ((x) >= 'a' && (x) <= 'z')
     
    const int maxn = 100009;
    const int maxl = 300009;
    const int C = 26;
     
    int V[maxn], seq[maxl], L[maxl], R[maxl];
    int N, node_n, Val, n;
    char s[maxl];
     
    inline int read() {
    char c = getchar();
    int ret = 0;
    for(; !isdigit(c); c = getchar());
    for(; isdigit(c); c = getchar())
    ret = ret * 10 + c - '0';
    return ret;
    }
     
    struct O {
    O* next;
    int v;
    } Opool[maxn], *Opt = Opool, *head[maxn];
     
    inline O* AddO(int x, int v) {
    Opt->v = v;
    Opt->next = head[x];
    return head[x] = Opt++;
    }
     
    struct node {
    node *ch[C], *f;
    O* t;
    int n;
    node() {
    memset(ch, 0, sizeof ch);
    f = 0;
    t = 0;
    }
    } Pool[maxl], *Pt, *Rt;
     
    inline node* newNode() {
    Pt->n = node_n++;
    return Pt++;
    }
     
    void Init_trie() {
    node_n = 0;
    Pt = Pool;
    Rt = newNode();
    }
     
    void Insert(int x, int len) {
    node* t = Rt;
    while(len--) {
    if(!t->ch[c(s[len])])
    t->ch[c(s[len])] = newNode();
    t = t->ch[c(s[len])];
    }
    t->t = AddO(V[x] = t->n, x + 1);
    }
     
    void Init() {
    Init_trie();
    n = 1;
    N = read();
    for(int i = 0; i < N; i++) {
    int len = 0;
    for(s[len] = getchar(); !chk(s[len]); s[len] = getchar());
    for(s[++len] = getchar(); chk(s[len]); s[++len] = getchar());
    Insert(i, len);
    }
    }
     
    void DFS(node* t) {
    L[t->n] = n + 1;
    for(O* x = head[t->n]; x; x = x->next)
    seq[++n] = x->v;
    for(int i = 0; i < C; i++)
    if(t->ch[i]) DFS(t->ch[i]);
    R[t->n] = n;
    }
     
    struct Node {
    Node *lc, *rc;
    int v;
    Node() {
    lc = rc = 0;
    }
    } pool[maxl * 40], *pt, *Root[maxl];
     
    void Init_sgt() {
    Root[0] = (pt = pool)++;
    Root[0]->lc = Root[0]->rc = Root[0];
    Root[0]->v = 0;
    }
     
    Node* Modify(Node* t, int l, int r) {
    Node* h = pt++;
    h->v = t->v + 1;
    if(l != r) {
    int m = (l + r) >> 1;
    if(Val <= m) {
    h->lc = Modify(t->lc, l, m);
    h->rc = t->rc;
    } else {
    h->lc = t->lc;
    h->rc = Modify(t->rc, m + 1, r);
    }
    }
    return h;
    }
     
    void Build() {
    for(int i = 1; i <= n; i++) if(seq[i]) {
    Val = seq[i];
    Root[i] = Modify(Root[i - 1], 1, N);
    } else
    Root[i] = Root[i - 1];
    }
     
    int Query(int x, int Rk) {
    Node *L = Root[::L[x] - 1], *R = Root[::R[x]];
    if(R->v - L->v < Rk)
    return -1;
    int l = 1, r = N;
    while(l < r) {
    int m = (l + r) >> 1;
    if(R->lc->v - L->lc->v < Rk) {
    Rk -= R->lc->v - L->lc->v;
    L = L->rc;
    R = R->rc;
    l = m + 1;
    } else {
    L = L->lc;
    R = R->lc;
    r = m;
    }
    }
    return l;
    }
     
    void Work() {
    for(int i = 0; i < N; i++)
    printf("%d ", Query(V[i], read()));
    }
     
    int main() {
    Init();
    DFS(Rt);
    Init_sgt();
    Build();
    Work();
    return 0;
    }

    ------------------------------------------------------------------

    3439: Kpm的MC密码

    Time Limit: 15 Sec  Memory Limit: 256 MB
    Submit: 311  Solved: 153
    [Submit][Status][Discuss]

    Description


     背景

        想Kpm当年为了防止别人随便进入他的MC,给他的PC设了各种奇怪的密码和验证问题(不要问我他是怎么设的。。。),于是乎,他现在理所当然地忘记了密码,只能来解答那些神奇的身份验证问题了。。。

     描述

        Kpm当年设下的问题是这样的:

        现在定义这么一个概念,如果字符串s是字符串c的一个后缀,那么我们称c是s的一个kpm串。

        系统将随机生成n个由a…z组成的字符串,由1…n编号(s1,s2…,sn),然后将它们按序告诉你,接下来会给你n个数字,分别为k1…kn,对于每一个ki,要求你求出列出的n个字符串中所有是si的kpm串的字符串的编号中第ki小的数,如果不存在第ki小的数,则用-1代替。(比如说给出的字符串是cd,abcd,bcd,此时k1=2,那么”cd”的kpm串有”cd”,”abcd”,”bcd”,编号分别为1,2,3其中第2小的编号就是2)(PS:如果你能在相当快的时间里回答完所有n个ki的查询,那么你就可以成功帮kpm进入MC啦~~)

    Input

     

        第一行一个整数 n 表示字符串的数目

        接下来第二行到n+1行总共n行,每行包括一个字符串,第i+1行的字符串表示编号为i的字符串

        接下来包括n行,每行包括一个整数ki,意义如上题所示

     

    Output

     

        包括n行,第i行包括一个整数,表示所有是si的kpm串的字符串的编号中第ki小的数

     

    Sample Input


    3
    cd
    abcd
    bcd
    2
    3
    1

    Sample Output

    2
    -1
    2

    样例解释

    “cd”的kpm 串有”cd”,”abcd”,”bcd”,编号为1,2,3,第2小的编号是

    2,”abcd”的kpm串只有一个,所以第3小的编号不存在,”bcd”的kpm

    串有”abcd”,”bcd”,第1小的编号就是2。

    数据范围与约定

    设所有字符串的总长度为len


    对于100%的数据,1<=n<=100000,0

    HINT

    Source

  • 相关阅读:
    HDU1013 Digital Roots
    DP---背包问题
    新生代与老年代
    JVM常见问题(二)
    JVM常见问题 一(转载)
    JVM内存模型及分区
    (转载)JVM知识小集
    类加载机制:全盘负责和双亲委托
    mysql 函数GROUP_CONCAT(temp.amount SEPARATOR ',')的用法
    mysql优化--explain关键字
  • 原文地址:https://www.cnblogs.com/JSZX11556/p/5092689.html
Copyright © 2011-2022 走看看