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 }     
View Code

 

posted @ 2019-06-19 20:17  zjxxcn  阅读(221)  评论(0编辑  收藏  举报