BZOJ 3439 Kpm的MC密码

Posted on 2016-11-12 20:22  ziliuziliu  阅读(235)  评论(0编辑  收藏  举报

倒着建trie,然后主席树来求子树第k大。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define maxn 300500
using namespace std;
int n,trie[maxn][27],fath[maxn],cnt=0,tag[maxn],k,rot,kr,dfn[maxn],mx[maxn],times=0,line[maxn];
int tot=0,ls[maxn<<3],rs[maxn<<3],sum[maxn<<3];
char s[maxn];
vector <int> v[maxn],root[maxn];
void insert(int father,int &now,int l,int x)
{
    if (!now) {now=++cnt;fath[now]=father;}
    if (!l) {tag[x]=now;v[now].push_back(x);return;}
    insert(now,trie[now][s[l-1]-'a'+1],l-1,x);
}
void build(int &now,int left,int right)
{
    now=++tot;
    if (left==right) return;
    int mid=left+right>>1;
    build(ls[now],left,mid);
    build(rs[now],mid+1,right);
}
void modify(int last,int &now,int left,int right,int pos)
{
    now=++tot;sum[now]=sum[last]+1;
    if (left==right) return;
    int mid=left+right>>1;
    ls[now]=ls[last];rs[now]=rs[last];
    if (pos<=mid) modify(ls[last],ls[now],left,mid,pos);
    else modify(rs[last],rs[now],mid+1,right,pos);
}
int ask(int last,int now,int left,int right,int x)
{
    if (sum[now]-sum[last]<x) return -1;
    if (left==right) return left;
    int mid=left+right>>1,lsum=sum[ls[now]]-sum[ls[last]];
    if (x<=lsum) return ask(ls[last],ls[now],left,mid,x);
    else return ask(rs[last],rs[now],mid+1,right,x-lsum);
}
void dfs(int now)
{
    dfn[now]=mx[now]=++times;line[times]=now;
    for (int i=1;i<=26;i++)
    {
        if (trie[now][i])
        {
            dfs(trie[now][i]);
            mx[now]=max(mx[now],mx[trie[now][i]]);
        }
    }
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%s",s);
        insert(0,rot,strlen(s),i);
    }
    dfs(rot);
    build(kr,1,n);root[0].push_back(1);
    for (int i=1;i<=times;i++)
    {
        int now=line[i];
        if (!v[now].size()) root[i].push_back(root[i-1][root[i-1].size()-1]);
        else
        {
            root[i].push_back(tot+1);modify(root[i-1][root[i-1].size()-1],kr,1,n,v[now][0]);
            for (int j=1;j<v[now].size();j++)
            {
                root[i].push_back(tot+1);
                modify(root[i][j-1],kr,1,n,v[now][j]);
            }
        }
    }
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&k);
        int ret=tag[i];
        printf("%d\n",ask(root[dfn[ret]-1][root[dfn[ret]-1].size()-1],root[mx[ret]][root[mx[ret]].size()-1],1,n,k));
    }
    return 0;
}