【loj#2133 && luoguP2178】[NOI2015]品酒大会

  题目传送门:loj#2133 luoguP2178

  简要题意:给定一个字符串\(s\),每个后缀都有权值,对于每个长度\(len\),求出所有最长公共前缀\(\geq len\)的后缀对的总数和每个这样的后缀对,两后缀权值乘积的最大值。

  我们可以先把后缀数组求出来,把最长公共前缀长度转化为height数组的区间\(\min\),考虑固定\(len\),容易发现合法的后缀对的取值范围被height数组上小于\(len\)的位置划分成了若干区间,同时当\(len\)变小的时候,相邻区间会发生合并。

  于是倒序枚举\(len\),用并查集辅助合并合法区间,同时统计贡献就可以解决问题了。

my code ```cpp #include #include #include #include #include #define ll long long #define inf 0x3f3f3f3f #define maxn 300010 inline ll read() { ll x=0; char c=getchar(),f=1; for(;c<'0'||'9'len)tsa[++tot]=sa[i]-len; memset(sum,0,sizeof(sum)); memcpy(trk,rk,sizeof(rk)); for(int i=1;i<=n;i++) ++sum[trk[tsa[i]]]; for(int i=1;i<=cnt;i++) sum[i]+=sum[i-1]; for(int i=n;i;i--) sa[sum[trk[tsa[i]]]--]=tsa[i]; cnt=1; rk[sa[1]]=1; for(int i=2;i<=n;i++){ if(trk[sa[i]]!=trk[sa[i-1]]||trk[sa[i]+len]!=trk[sa[i-1]+len])++cnt; rk[sa[i]]=cnt; } } for(int i=1;i<=n;i++) if(rk[i]>1){ lcp[rk[i]]=std::max(lcp[rk[i-1]]-1,0); while(s[i+lcp[rk[i]]]==s[sa[rk[i]-1]+lcp[rk[i]]])++lcp[rk[i]]; } } bool cmp(int x,int y){return lcp[x]>lcp[y];} int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} void modify(int now) { int p1=find(now-1),p2=find(now); fa[p1]=p2; tot-=(ll)(r[p1]-l[p1]+1)*(r[p1]-l[p1])/2+(ll)(r[p2]-l[p2]+1)*(r[p2]-l[p2])/2; l[p2]=l[p1]; if(mx1[p1]>mx1[p2]){ int t1=mx1[p1],t2=std::max(mx1[p2],mx2[p1]); mx1[p2]=t1; mx2[p2]=t2; } else{ int t1=mx1[p2],t2=std::max(mx1[p1],mx2[p2]); mx1[p2]=t1; mx2[p2]=t2; } if(mn1[p1]=0;i--){ while(now<=n&&lcp[id[now]]>=i)modify(id[now++]); cnt[i]=tot; mx[i]=cur; } for(int i=0;i
posted @ 2019-12-06 21:35  QuartZ_Z  阅读(78)  评论(0编辑  收藏