【BZOJ3439】Kpm的MC密码,trie树+dfs序+主席树

Time:2016.05.07
Author:xiaoyimi
转载注明出处谢谢

传送门
思路:
1.have a trie,还得是倒着建的,记录每个结尾节点的id(可能会有重复,所以开一个vector记录一下)
2.对trie树进行dfs序,记录结尾节点的子树区间
3.建立主席树(由于权值就是id标号,所以不用离散化了)
4.1-n依次查询即可
注意:
dfs序在遇到结尾节点时才++记录序号的变量cnt,而且相同串的dfs序号是一样的(就是说一个结尾节点可能包含好多个不同编号的串),左区间取最前的一个(比如dfs到x节点,编号为2,x节点存着两个串,那么它们的左区间都为2,而序号要加两次)
代码:

#include<bits/stdc++.h>
using namespace std;
#define M 100004
#define ls(x) a[x].ch[0]
#define rs(x) a[x].ch[1]
int n,m,cnt,root=M*3-10;
int trie[M*3][26],num[M*3],Ls[M],Rs[M],data[M];
vector <int> q[M*3];
char s[M*3];
struct Chairman_tree
{
    int siz,ch[2];
}a[M*18];
int in()
{
    int t=0;char ch=getchar();
    while (!isdigit(ch)) ch=getchar();
    while (isdigit(ch)) t=(t<<3)+(t<<1)+ch-48,ch=getchar();
    return t;
}
void build(int now,int L,int R,int rt,int val)
{
    a[rt].siz=a[now].siz+1;
    if (L==R) return;
    int mid=(L+R)>>1;
    if (mid>=val)
        rs(rt)=rs(now),
        ls(rt)=++cnt,
        build(ls(now),L,mid,ls(rt),val);
    else
        ls(rt)=ls(now),
        rs(rt)=++cnt,
        build(rs(now),mid+1,R,rs(rt),val);
}
int get(int begin,int end,int L,int R,int k)
{
    if (begin==end) return end;
    int mid=(begin+end)>>1,t=a[ls(R)].siz-a[ls(L)].siz;
    if (k<=t)
        return get(begin,mid,ls(L),ls(R),k);
    else
        return get(mid+1,end,rs(L),rs(R),k-t);
}
void insert(int id,char s[])
{
    int len=strlen(s),now=root;
    for (int i=len-1;i>=0;i--)
    {
        if (!trie[now][s[i]-'a']) trie[now][s[i]-'a']=++cnt;
        now=trie[now][s[i]-'a'];
    }
    num[now]++;
    q[now].push_back(id);
}
void dfs(int x)
{
    if (num[x])
    {       
        for (int i=0;i<num[x];i++)
            Ls[q[x][i]]=cnt+1;
        for (int i=0;i<num[x];i++)
            data[++cnt]=q[x][i];
    }
    for (int i=0;i<26;i++)
        if (trie[x][i]) dfs(trie[x][i]);
    for (int i=0;i<num[x];i++)
        Rs[q[x][i]]=cnt;
}
main()
{
    n=in();
    for (int i=1;i<=n;i++)
        scanf("%s",s),
        insert(i,s);
    cnt=0;
    dfs(root);
    cnt=n+1;
    int k;
    for (int i=1;i<=n;i++) build(i,1,n,i+1,data[i]);
    for (int i=1;i<=n;i++)
    {
        k=in();
        if (a[Rs[i]+1].siz-a[Ls[i]].siz<k) printf("-1\n");
        else printf("%d\n",get(1,n,Ls[i],Rs[i]+1,k));
    }
}

你可能感兴趣的:(【BZOJ3439】Kpm的MC密码,trie树+dfs序+主席树)