1. 程式人生 > >解題:TJOI 2015 弦論

解題:TJOI 2015 弦論

題面

好像是個經典問題,然而我沒做過

建SAM,然後經過每個節點的子串數目就可以求了,多個相同子串算一個的話就把所有siz都搞成$1$,否則就是$right$集合的大小,然後就是常見的遞推

求第$k$小是從根節點出發按字典序沿著trans往下走,每次輸出對應的字元然後扣掉對應的數量

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=500005,M=1000005;
 6 int fth[M],trs[M][26
],len[M],siz[M]; 7 int rnk[M],bkt[M]; long long sum[M]; 8 int typ,kth,lth,lst,tot; 9 char str[N]; 10 void Insert(int ch) 11 { 12 int nde=lst,newn=++tot; lst=newn; 13 siz[newn]=1,len[newn]=len[nde]+1; 14 while(nde&&!trs[nde][ch]) 15 trs[nde][ch]=newn,nde=fth[nde]; 16 if
(!nde) 17 fth[newn]=1; 18 else 19 { 20 int tran=trs[nde][ch]; 21 if(len[tran]==len[nde]+1) 22 fth[newn]=tran; 23 else 24 { 25 int rnde=++tot; len[rnde]=len[nde]+1; 26 for(int i=0;i<=25;i++) trs[rnde][i]=trs[tran][i];
27 fth[rnde]=fth[tran],fth[tran]=fth[newn]=rnde; 28 while(nde&&trs[nde][ch]==tran) 29 trs[nde][ch]=rnde,nde=fth[nde]; 30 } 31 } 32 } 33 int main() 34 { 35 register int i,j,k; 36 scanf("%s%d%d",str+1,&typ,&kth); 37 lth=strlen(str+1),lst=tot=1; 38 for(i=1;i<=lth;i++) Insert(str[i]-'a'); 39 for(i=1;i<=tot;i++) bkt[len[i]]++; 40 for(i=1;i<=lth;i++) bkt[i]+=bkt[i-1]; 41 for(i=1;i<=tot;i++) rnk[bkt[len[i]]--]=i; 42 for(i=tot;i;i--) 43 j=rnk[i],typ?siz[fth[j]]+=siz[j]:siz[j]=1; 44 siz[1]=0; 45 for(i=tot;i;i--) 46 { 47 j=rnk[i],sum[j]=siz[j]; 48 for(k=0;k<=25;k++) 49 if(trs[j][k]) sum[j]+=sum[trs[j][k]]; 50 } 51 if(kth>sum[1]) printf("-1"); 52 else 53 { 54 int nde=1; 55 while(kth-siz[nde]>0) 56 { 57 kth-=siz[nde]; 58 for(i=0;i<=25&&kth>sum[trs[nde][i]];i++) 59 kth-=sum[trs[nde][i]]; 60 nde=trs[nde][i],printf("%c",i+'a'); 61 } 62 } 63 return 0; 64 }
View Code