BZOJ3439 Kpm的MC密码(可持久化trie)

  将串反过来就变成查询前缀了。考虑建一棵可持久化trie,查询时二分答案,均摊一下复杂度即为O(mlogn)。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
#define N 100010
#define M 300010
int n,cnt=0,root[N],size[N];
vector<int> a[N];
struct data{int ch[26],x;
}tree[M<<1];
void ins(int &k,int x,int p)
{
    tree[++cnt]=tree[k],k=cnt;tree[k].x++;
    if (p==size[x]) return;
    ins(tree[k].ch[a[x][p]],x,p+1);
}
int query(int k,int x,int p)
{
    if (!k) return 0;
    if (p==size[x]) return tree[k].x;
    return query(tree[k].ch[a[x][p]],x,p+1);
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj3439.in","r",stdin);
    freopen("bzoj3439.out","w",stdout);
    const char LL[]="%I64d
";
#else
    const char LL[]="%lld
";
#endif
    n=read();
    for (int i=1;i<=n;i++)
    {
        char c=getchar();
        while (c<'a'||c>'z') c=getchar();
        while (c>='a'&&c<='z') a[i].push_back(c-'a'),c=getchar();
        root[i]=root[i-1];size[i]=a[i].size();
        reverse(a[i].begin(),a[i].end());
        ins(root[i],i,0);
    }
    for (int i=1;i<=n;i++)
    {
        int x=read();
        int l=1,r=n,ans=-1;
        while (l<=r)
        {
            int mid=l+r>>1;
            if (query(root[mid],i,0)>=x) ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("%d
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Gloid/p/9682966.html