【學術篇】NOI2015 品酒大會 字尾陣列+並查集
阿新 • • 發佈:2018-12-31
省選前大致是刷不了幾道題了... 所以就找一些裸一點的題目練練板子算了= =
然而這題一點都不裸, 也並不怎麼好寫... 於是就浪費了將近一下午的時間... 然而還不是因為字尾陣列板子不熟= =
首先這個"r相似"很顯然就是lcp的值, 也就能想到字尾陣列上的height... 不會後綴陣列的先左轉百度~
那麼我們考慮如果有一個連續的區間, 它們的height值都是大於等於r的, 那麼這段區間中的字尾兩兩"r相似".
而"r相似"的話, 也肯定有"r-1相似", "r-2相似", ... "0相似". 這樣我們就會重複統計, 就會浪費時間. 所以我們不妨將這個連續的區間表示成一個點, 並查集!!
這樣我們把id按照對應位置的height降序排序, 然後對於每個id, 我們把id-1這個點所在的區間和id所在的區間合併(根據height的含義, 就是表示sa[id]和sa[id-1]所對應的字尾的lcp長度..)
合併的同時維護資訊即可. 說起來挺輕巧的, 其實不是很好懂..
最後不要忘了統計的時候做一個字尾和, 比r大的答案都要統計一下.
說的很不清楚(然而其實只是用來練板子誰曾想到這破題我寫了一下午呢...)
有不懂的可以去看程式碼看了就更不懂了Emmmm
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N=300050; const long long INF=1ll<<62; char s[N]; int fa[N],sz[N],mn[N],mx[N],w[N],id[N]; long long cnt[N],ans[N]; int x[N],y[N],sa[N],rnk[N],cc[N],height[N],len; inline int gn(int a=0,char c=0,int f=1){ for(;(c<'0'||c>'9')&&c!='-';c=getchar()); if(c=='-') c=getchar(),f=-1; for(;c>47&&c<58;c=getchar()) a=a*10+c-48; return a*f;} bool hcmp(int x,int y){ if(height[x]==height[y]) return x<y; return height[x]>height[y]; } bool cmp(int *y,int a,int b,int k){ int ra=a+k>=len?-1:y[a+k],rb=b+k>=len?-1:y[b+k]; return y[a]==y[b]&&ra==rb; } void make_sa(){ int m=26; for(int i=0;i<m;++i) cc[i]=0; for(int i=0;i<len;++i) ++cc[x[i]=s[i]-'a']; for(int i=1;i<m;++i) cc[i]+=cc[i-1]; for(int i=len-1;i>=0;--i) sa[--cc[x[i]]]=i; for(int k=1;k<=len;k<<=1){ int p=0; for(int i=len-k;i<len;++i) y[p++]=i; for(int i=0;i<len;++i) if(sa[i]>=k) y[p++]=sa[i]-k; for(int i=0;i<m;++i) cc[i]=0; for(int i=0;i<len;++i) ++cc[x[y[i]]]; for(int i=1;i<m;++i) cc[i]+=cc[i-1]; for(int i=len-1;i>=0;--i) sa[--cc[x[y[i]]]]=y[i]; std::swap(x,y); m=1; x[sa[0]]=0; for(int i=1;i<len;++i) x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?m-1:m++; if(m>=len) break; } for(int i=0;i<len;++i) rnk[sa[i]]=i; } void make_height(){ int k=0; for(int i=0;i<len;++i){ if(!rnk[i]) continue; int j=sa[rnk[i]-1]; if(k) --k; while(s[i+k]==s[j+k]) ++k; height[rnk[i]]=k; } } int find(int x){ if(fa[x]!=x) fa[x]=find(fa[x]); return fa[x];} void merge(int x,int y){ sz[y]+=sz[x]; fa[x]=y; mn[y]=min(mn[x],mn[y]); mx[y]=max(mx[x],mx[y]); } int main(){ len=gn(); scanf("%s",s); len=strlen(s); make_sa(); make_height(); for(int i=0;i<len;++i) w[i]=gn(); for(int i=0;i<len;++i){ fa[i]=i; id[i]=i; mx[i]=w[sa[i]]; mn[i]=w[sa[i]]; sz[i]=1; ans[i]=-INF; } ::sort(id+1, id+len, hcmp); for(int i=1;i<len;++i){ int x=find(id[i]-1),y=find(id[i]); cnt[height[id[i]]]+=1ll*sz[x]*sz[y]; ans[height[id[i]]]=max(ans[height[id[i]]],1ll*mn[x]*mn[y]); ans[height[id[i]]]=max(ans[height[id[i]]],1ll*mn[x]*mx[y]); ans[height[id[i]]]=max(ans[height[id[i]]],1ll*mx[x]*mn[y]); ans[height[id[i]]]=max(ans[height[id[i]]],1ll*mx[x]*mx[y]); merge(x,y); } for(int i=len-2;i>=0;--i) cnt[i]+=cnt[i+1],ans[i]=max(ans[i],ans[i+1]); for(int i=0;i<len;++i) printf("%lld %lld\n",cnt[i],cnt[i]?ans[i]:0); }