日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

BZOJ3998 TJOI2015弦论(后缀数组+二分答案)

發(fā)布時間:2023/12/20 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 BZOJ3998 TJOI2015弦论(后缀数组+二分答案) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

  先看t=1的情況。顯然得求出SA(因為我不會SAM)。我們一位位地確定答案。設填到了第len位,二分這一位填什么之后,在已經(jīng)確定的答案所在的范圍(SA上的某段區(qū)間)內(nèi)二分,找到最后一個小于當前串的后綴,那么從區(qū)間左端點到該位置的這些后綴的所有前綴都要比二分出的答案小,判一下是否合法。確定了這一位填什么之后,還要找到最后一個前l(fā)en位小于等于當前串的后綴,若加上這一部分后比答案串小的已經(jīng)超過k個的話,則答案已經(jīng)確定可以直接退出了,否則將這些計入并繼續(xù)填下一位,更新這些前l(fā)en位等于答案串的后綴為答案范圍。注意算的時候要減去已經(jīng)計入的部分。

  t=0類似,算出height數(shù)組后可以去掉重復子串。注意一些細節(jié)就好。

  時間復雜度O(nlognlog|s|),s為字符集大小也就是26,跑的還挺快。

  我怎么還不學SAM啊。

#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() {int x=0,f=1;char c=getchar();while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();return x*f; } #define N 500010 char a[N],ans[N]; int T,k,n,sa[N],sa2[N<<1],rk[N<<1],tmp[N<<1],cnt[N],h[N]; long long sum[N]; void make() {memset(cnt,0,sizeof(cnt));int m=0;for (int i=1;i<=n;i++) cnt[rk[i]=a[i]]++,m=max(m,(int)a[i]);for (int i=1;i<=m;i++) cnt[i]+=cnt[i-1];for (int i=n;i>=1;i--) sa[cnt[rk[i]]--]=i;for (int k=1;k<=n;k<<=1){int p=0;for (int i=n-k+1;i<=n;i++) sa2[++p]=i;for (int i=1;i<=n;i++) if (sa[i]>k) sa2[++p]=sa[i]-k;memset(cnt,0,m+1<<2);for (int i=1;i<=n;i++) cnt[rk[i]]++;for (int i=1;i<=m;i++) cnt[i]+=cnt[i-1];for (int i=n;i>=1;i--) sa[cnt[rk[sa2[i]]]--]=sa2[i];memcpy(tmp,rk,sizeof((rk)));p=1;rk[sa[1]]=1;for (int i=2;i<=n;i++){if (tmp[sa[i]]!=tmp[sa[i-1]]||tmp[sa[i]+k]!=tmp[sa[i-1]+k]) p++;rk[sa[i]]=p;}if (p>=n) break;m=p;} } int lower_find(int len,int l,int r,char c) {int ans=l-1;while (l<=r){int mid=l+r>>1;if (a[sa[mid]+len-1]<c) ans=mid,l=mid+1;else r=mid-1;}return ans; } int upper_find(int len,int l,int r,char c) {int ans=l;while (l<=r){int mid=l+r>>1;if (a[sa[mid]+len-1]<=c) ans=mid,l=mid+1;else r=mid-1;}return ans; } int main() {freopen("bzoj3998.in","r",stdin);freopen("bzoj3998.out","w",stdout);scanf("%s",a+1);n=strlen(a+1);cin>>T>>k;make();int len=0;if (T==1){for (int i=1;i<=n;i++) sum[i]=sum[i-1]+n-sa[i]+1;int l=1,r=n;while (len<=n){len++;char lc='a',rc='z';while (lc<=rc){char midc=lc+rc>>1;int x=lower_find(len,l,r,midc);if (sum[x]-sum[l-1]-(len-1)*(x-l+1)<k) lc=midc+1,ans[len]=midc;else rc=midc-1;}int x=lower_find(len,l,r,ans[len]),y=upper_find(len,l,r,ans[len]);k-=sum[x]-sum[l-1]-(len-1)*(x-l+1)+y-x;if (k<=0) break;l=x+1,r=y;}}else{for (int i=1;i<=n;i++){h[i]=max(h[i-1]-1,0);while (a[i+h[i]]==a[sa[rk[i]-1]+h[i]]) h[i]++;}for (int i=1;i<=n;i++) sum[i]=sum[i-1]+n-sa[i]+1-h[sa[i]];int l=1,r=n;while (len<=n){len++;char lc='a',rc='z';while (lc<=rc){char midc=lc+rc>>1;int x=lower_find(len,l,r,midc);if ((x==l-1?0:sum[x]-sum[l-1]+h[sa[l]]-len+1)<k) lc=midc+1,ans[len]=midc;else rc=midc-1;}int x=lower_find(len,l,r,ans[len]),y=upper_find(len,l,r,ans[len]);k-=(x==l-1?0:sum[x]-sum[l-1]+h[sa[l]]-len+1)+1;if (k<=0) break;l=x+1,r=y;}}if (k>0) cout<<-1;else for (int i=1;i<=len;i++) printf("%c",ans[i]);fclose(stdin);fclose(stdout);return 0; }

?

轉(zhuǎn)載于:https://www.cnblogs.com/Gloid/p/9406593.html

總結(jié)

以上是生活随笔為你收集整理的BZOJ3998 TJOI2015弦论(后缀数组+二分答案)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。