题解:
咳咳。首先补全数据范围:对于100%的数据,1<=n<=100000,0<len<=300000
然后乍一看题(BZ少数据范围),直接排个序,然后插入点信息来一发可持久化线段树求区间第K大、
显然存不下,那么我们可以用Trie存一下这些字符串,然后dfs序扫一遍,确定一个字符串的可查询范围。
话说用Trie树存的是反串(后缀么)
呃,今天生病了,有点犯浑,语文能力可能下降了许多。。
所以还是看代码吧。
完了我已经病入膏肓,可持久化线段树空间开O(n)还好几遍都觉得“并没有错”!
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 101000
#define LOGN 20
#define M 301000
#define T 26
#define ls son[x][0]
#define rs son[x][1]
using namespace std;
int e[N],head[M],cnt;
inline void add(int u)
{
e[++cnt]=head[u];
head[u]=cnt;
}
int n,L[N],R[N];
struct Functional_Tree
{
int root[N*LOGN],son[N*LOGN][2],size[N*LOGN],cnt,Last;
int insert(int last,int l,int r,int p)
{
int x=++cnt;
if(l==r){size[x]=1;return x;}
int mid=l+r>>1;
if(p<=mid)ls=insert(son[last][0],l,mid,p),rs=son[last][1];
else rs=insert(son[last][1],mid+1,r,p),ls=son[last][0];
size[x]=size[ls]+size[rs];
return x;
}
void Insert(int id)
{
root[Last+1]=insert(root[Last],1,n,id);
Last++;
}
int query(int last,int now,int l,int r,int k)
{
int mid=l+r>>1;
if(size[now]-size[last]<k)return -1;
if(l==r)return l;
int temp=size[son[now][0]]-size[son[last][0]];
if(temp>=k)return query(son[last][0],son[now][0],l,mid,k);
else return query(son[last][1],son[now][1],mid+1,r,k-temp);
}
int Query(int i,int k){return query(root[L[i]],root[R[i]],1,n,k);}
}fct;
struct Trie
{
int next[M][T],cnt,tot;
char s[M];
void insert()
{
int i,x=0,alp,len;
scanf("%s",s),len=strlen(s);
for(i=len-1;i>=0;i--)
{
alp=s[i]-'a';
if(!next[x][alp])next[x][alp]=++cnt;
x=next[x][alp];
}
add(x);
}
void dfs(int x)
{
int i,v,rem=tot;
for(i=head[x];i;i=e[i])
{
tot++;
fct.Insert(i);
}
for(i=0;i<26;i++)if(v=next[x][i])dfs(v);
for(i=head[x];i;i=e[i])L[i]=rem,R[i]=tot;
}
}trie;
int main()
{
freopen("test.in","r",stdin);
int i,j,k;
for(scanf("%d",&n),i=1;i<=n;i++)trie.insert();
trie.dfs(0);
for(i=1;i<=n;i++)
{
scanf("%d",&k);
printf("%d\n",fct.Query(i,k));
}
return 0;
}