bzoj3439: Kpm的MC密码

开始本来以为是AC自动机,后来发现不行,跳fail的时候没用的状态太多了。

正解倒着建Trie

然后答案就在子树里

dfs序+主席树求子树中第k大

注意有重串,用链表搞一下

1A,表扬自己~

 

trie和主席树的变量名重了改的好丑。。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;

struct Trie
{
    int w[30],last,fail;
    void clean(){memset(w,0,sizeof(w));last=0;}
}tr[310000];int trlen,next[110000];
char ss[110000];
void insert(int id)
{
    int now=0,len=strlen(ss+1);
    for(int i=len;i>=1;i--)
    {
        int x=ss[i]-'a'+1;
        if(tr[now].w[x]==0)
            tr[now].w[x]=++trlen, tr[trlen].clean();
        now=tr[now].w[x];
    }
    next[id]=tr[now].last;
    tr[now].last=id;
}

//-------------Trie--------------------

struct chairman_tree
{
    int lc,rc,c;
}ch[6100000];int chlen,rt[310000];
int maketree(int now,int l,int r,int p)
{
    if(now==0)
    {
        now=++chlen;
        ch[now].lc=ch[now].rc=0;
        ch[now].c=0;
    }
    ch[now].c++;
    if(l==r)return now;
    else
    {
        int mid=(l+r)/2;
        if(p<=mid)ch[now].lc=maketree(ch[now].lc,l,mid,p);
        else       ch[now].rc=maketree(ch[now].rc,mid+1,r,p);
        return now;
    }
}
int merge(int x,int y)
{
    if(x==0||y==0)return x+y;
    ch[x].c+=ch[y].c;
    ch[x].lc=merge(ch[x].lc,ch[y].lc);
    ch[x].rc=merge(ch[x].rc,ch[y].rc);
    return x;
}
//init~~~~~~~

int getk(int x,int y,int l,int r,int k)
{
    if(k>ch[y].c-ch[x].c)return -1;
    if(l==r)return l;
    
    int lsum=ch[ch[y].lc].c-ch[ch[x].lc].c;
    int mid=(l+r)/2;
    if(lsum>=k)return getk(ch[x].lc,ch[y].lc,l,mid,k);
    else        return getk(ch[x].rc,ch[y].rc,mid+1,r,k-lsum);
}

//--------------chairman tree-----------------

const int m=1001000;
int z,l[310000],r[310000];
int hh[110000];
void dfs(int now)
{
    l[now]=++z;
    
    for(int k=tr[now].last;k;k=next[k])
    {
        rt[z]=maketree(rt[z],1,m,k);
        hh[k]=now;
    }
    rt[z]=merge(rt[z],rt[z-1]);
    
        
    for(int x=1;x<=26;x++)
    {
        int son=tr[now].w[x];
        if(son!=0)dfs(son);
    }
    
    r[now]=z;
}

int main()
{
    int n;
    scanf("%d",&n);
    trlen=0;tr[trlen].clean();
    for(int i=1;i<=n;i++)
    {
        scanf("%s",ss+1);
        insert(i);
    }
    memset(rt,0,sizeof(rt));
    z=0;dfs(0);
    
    int k;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&k);
        printf("%d\n",getk(rt[l[hh[i]]-1],rt[r[hh[i]]],1,m,k));
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/AKCqhzdy/p/8873439.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值