Codechef January Challenge 2018 - Killjee and k-th letter
题意:
给出一个的串 s,将 s 所有子串按照字典序排列好相接起来形成一个新串q次询问,每一次询问问新串中的第 k 个字符是什么,强制在线。
$|s|,q \le 2*10^{5} $
跟所有子串有关,那肯定要么是后缀自动机,要么是后缀树。
考虑后缀自动机。即使后缀自动机单次询问可以做到线性,在这题也无施展之地。鉴于他DAWG的性质,没有什么东西可以维护。
然而后缀树就不一样了,[TJOI2015] 弦论有一种O(n+logn)的做法,可以参考Mangoyang的博客。
实际上考虑 parent 可以进一步优化算法的复杂度,考虑原先的 parent 树一个节点代表的多个串都是最长的串的一个后缀,是一棵类似于前缀树的结构,这样不能适用于一些字典序上优美的性质。不妨将串反序插入到sam 中,这样每一个点能代表的多个串都是最长的串的前缀,这些串从长到短在字典序上一定是有序的。扩展到整棵树上,根据 minlen(u)=len(fa(u))+1 ,每个点代表的字符串都比其祖先代表的字符串的字典序大。于是可以计算出每一棵子树代表了多少串,在 dfn 序上二分答案即可
类似的,也可以计算出后缀树每一颗子树代表的串的总长,并且通过构造可以使后缀树上字符串的字典序与 dfn 序同时有序。这样找到了后缀树上的一个节点后,考虑一个子串其所代表的串长度在 [len(fa(u))+1,len(u)] 上连续,在这个节点上继续二分答案就可以找到第k个字符所在的子串及它的长度,再计算一下就能知道第k个字符在s中的位置了。时间复杂度O(n+qlogn)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 int const N=200000+10; 5 struct node{ 6 int len,fa,ch[26]; 7 }a[N<<1]; 8 int tot,ls,w[N<<1],num[N],sa[N<<1],cnt,pos[N<<1],son[N<<1][26]; 9 ll sum[N<<1],l[N<<1],sz[N<<1],qs[N<<1]; 10 char s[N]; 11 void add(int c,int id){ 12 int p=ls; 13 int np=ls=++tot; 14 a[np].len=a[p].len+1; 15 w[tot]=id; 16 sz[tot]=1; 17 for(;p&&!a[p].ch[c];p=a[p].fa) a[p].ch[c]=np; 18 if(!p) a[np].fa=1; 19 else { 20 int q=a[p].ch[c]; 21 if(a[q].len==a[p].len+1) a[np].fa=q; 22 else { 23 int nq=++tot; 24 a[nq]=a[q]; 25 a[nq].len=a[p].len+1; 26 a[q].fa=a[np].fa=nq; 27 for(;p&&a[p].ch[c]==q;p=a[p].fa) 28 a[p].ch[c]=nq; 29 } 30 } 31 } 32 void dfs(int x){ 33 if(!x ) return ; 34 pos[++cnt]=x; 35 for(int i=0;i<26;i++) 36 dfs(son[x][i]); 37 } 38 int main(){ 39 scanf("%s",s); 40 int len=strlen(s); 41 tot=ls=1; 42 for(int i=len-1;i>=0;i--) 43 add(s[i]-'a',i+1); 44 for(int i=2;i<=tot;i++) 45 sum[i]=1; 46 for(int i=1;i<=tot;i++) num[a[i].len]++; 47 for(int i=1;i<=len;i++) num[i]+=num[i-1]; 48 for(int i=1;i<=tot;i++) sa[num[a[i].len]--]=i; 49 for(int i=tot;i>=1;i--){ 50 int x=sa[i]; 51 int f=a[x].fa; 52 sz[f]+=sz[x]; 53 w[f]=w[f ]? w[f]:w[x]; 54 } 55 for(int i=2;i<=tot;i++){ 56 int x=a[i].fa; 57 int y=s[w[i]+a[a[i].fa].len-1]-'a'; 58 son[x][y]=i; 59 } 60 dfs(1); 61 for(int i=2;i<=tot;i++) { 62 int t=pos[i]; 63 ll x=a[a[t].fa].len+1; 64 ll y=a[t].len; 65 ll tmp=sz[t]*(x+y)*(y-x+1)/2; 66 qs[i]=qs[i-1]+tmp; 67 } 68 69 int q; 70 scanf("%d",&q); 71 ll g=0; 72 while (q--){ 73 ll p,m; 74 scanf("%lld%lld",&p,&m); 75 ll k=p*g % m +1; 76 int t=lower_bound(qs+1,qs+tot+1,k)-qs; 77 k-=qs[t-1]; 78 t=pos[t]; 79 ll x=a[a[t].fa].len+1; 80 ll y=a[t].len; 81 ll tmp=sz[t]*(x+y)*(y-x+1)/2; 82 ll l=x,r=y; 83 while (l<r){ 84 ll mid=(l+r)/2; 85 tmp=sz[t]*(x+mid)*(mid-x+1)/2; 86 if(tmp>=k) r=mid; 87 else l=mid+1; 88 } 89 k-=sz[t]*(x+r-1)*(r-x)/2; 90 k=(k-1) % r+1; 91 int c=w[t]+k-2; 92 printf("%c\n",s[c]); 93 g+=s[c]; 94 } 95 return 0; 96 }