题解:
咳咳。首先补全数据范围:对于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; }