bzoj 4199: [Noi2015]品酒大会 后缀数组+并查集
来源:互联网 发布:淘宝卖会员账号 编辑:程序博客网 时间:2024/06/05 17:59
题意
给出一个 长度为 n 的字符串,每一位有一个权值 val。定义两个位字符为 r 相似,是指分别从这两个字符开始,到后面的 r 个字符都相等。两个 r 相似的字符还有一个权值为这两个字符权值的乘积。问对于 r = 0, 1, 2, … , n - 1,统计出有多少种方法可以选出 2 个“r 相似”的字符,并回答选择 2 个”r 相似”的字符可以得到的权值的最大值。
n<=3*10^5,val <=10^9
分析
先构造出height数组,然后把height数组从大到小排序,并逐个加入。每加入一个就用并查集维护一下数量,最大值次大值,最小值次小值并计算一波贡献即可。
一开始用vector来排序height数组结果T了,改成排序才过。。。
代码
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;typedef long long LL;const int N=300005;const int inf=0x7fffffff;int n,b[N],c[N],d[N],rank[N*2],sa[N],height[N],f[N],size[N],mx1[N],mx2[N],mn1[N],mn2[N],a[N],num[N];LL ans[N],mx[N];char s[N];void get_sa(int n,int m){ for (int i=1;i<=n;i++) b[s[i]]++; for (int i=1;i<=m;i++) b[i]+=b[i-1]; for (int i=n;i>=1;i--) c[b[s[i]]--]=i; int t=0; for (int i=1;i<=n;i++) { if (s[c[i]]!=s[c[i-1]]) t++; rank[c[i]]=t; } int j=1; while (j<=n) { for (int i=1;i<=n;i++) b[i]=0; for (int i=1;i<=n;i++) b[rank[i+j]]++; for (int i=1;i<=n;i++) b[i]+=b[i-1]; for (int i=n;i>=1;i--) c[b[rank[i+j]]--]=i; for (int i=1;i<=n;i++) b[i]=0; for (int i=1;i<=n;i++) b[rank[i]]++; for (int i=1;i<=n;i++) b[i]+=b[i-1]; for (int i=n;i>=1;i--) d[b[rank[c[i]]]--]=c[i]; t=0; for (int i=1;i<=n;i++) { if (rank[d[i]]!=rank[d[i-1]]||rank[d[i]]==rank[d[i-1]]&&rank[d[i]+j]!=rank[d[i-1]+j]) t++; c[d[i]]=t; } for (int i=1;i<=n;i++) rank[i]=c[i]; if (t==n) break; j*=2; } for (int i=1;i<=n;i++) sa[rank[i]]=i;}void get_height(int n){ int k=0; for (int i=1;i<=n;i++) { if (k) k--; int j=sa[rank[i]-1]; while (i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++; height[rank[i]]=k; }}int find(int x){ if (f[x]==x) return x; f[x]=find(f[x]); return f[x];}LL calc(int x){ return (LL)size[x]*(size[x]-1)/2;}void merge(int x,int y){ size[y]+=size[x];f[x]=y; if (mx1[x]!=-inf) { int w=mx1[x]; if (w>mx1[y]) mx2[y]=mx1[y],mx1[y]=w; else if (w>mx2[y]) mx2[y]=w; } if (mx2[x]!=-inf) { int w=mx2[x]; if (w>mx1[y]) mx2[y]=mx1[y],mx1[y]=w; else if (w>mx2[y]) mx2[y]=w; } if (mn1[x]!=inf) { int w=mn1[x]; if (w<mn1[y]) mn2[y]=mn1[y],mn1[y]=w; else if (w<mn2[y]) mn2[y]=w; } if (mn2[x]!=inf) { int w=mn2[x]; if (w<mn1[y]) mn2[y]=mn1[y],mn1[y]=w; else if (w<mn2[y]) mn2[y]=w; }}bool cmp(int a,int b){ return height[a]>height[b];}int main(){ scanf("%d",&n); scanf("%s",s+1); for (int i=1;i<=n;i++) scanf("%d",&a[i]); get_sa(n,200); get_height(n); for (int i=1;i<n;i++) num[i]=i+1; sort(num+1,num+n,cmp); for (int i=1;i<=n;i++) f[i]=i,mn1[i]=mx1[i]=a[sa[i]],mn2[i]=inf,mx2[i]=-inf,size[i]=1; for (int i=0;i<=n;i++) mx[i]=-(LL)inf*inf; int p=1; for (int i=n-1;i>=0;i--) { ans[i]=ans[i+1];mx[i]=mx[i+1]; while (height[num[p]]==i&&p<n) { int x=num[p]; ans[i]-=calc(find(x))+calc(find(x-1)); merge(find(x),find(x-1)); int w=find(x); ans[i]+=calc(w); mx[i]=max(mx[i],max((LL)mx1[w]*mx2[w],(LL)mn1[w]*mn2[w])); p++; } } for (int i=0;i<=n-1;i++) printf("%lld %lld\n",ans[i],mx[i]==-(LL)inf*inf?0:mx[i]); return 0;}
0 0
- bzoj 4199: [Noi2015]品酒大会 后缀数组+并查集
- [Noi2015]品酒大会|后缀数组|并查集
- BZOJ_P4199 [NOI2015] 品酒大会(后缀数组+并查集)
- [BZOJ4199][Noi2015]品酒大会(后缀数组+并查集)
- [UOJ 131]【NOI2015】品酒大会:后缀数组+并查集
- bzoj4199 [Noi2015]品酒大会 后缀数组+并查集
- bzoj4199[luoguP2178]品酒大会[noi2015] (后缀数组+并查集)
- NOI2015.品酒大会(后缀数组)
- NOI2015品酒大会 后缀数组
- BZOJ 4199 [Noi2015]品酒大会
- 【BZOJ4199】品酒大会,后缀数组+并查集维护
- [BZOJ4199][NOI2015]品酒大会 后缀数组
- [BZOJ4199][NOI2015]品酒大会-后缀数组
- 【BZOJ 4199】[Noi2015]品酒大会 后缀自动机构造后缀树+dp
- 4199: [Noi2015]品酒大会
- 【bzoj4199】[Noi2015]品酒大会 后缀自动机
- 洛谷 P2178 品酒大会(bzoj P4199 [Noi2015]品酒大会/uoj P131【NOI2015】品酒大会)
- [BZOJ4199][NOI2015]品酒大会(后缀数组+单调栈+ST表)
- 响应式布局中边框问题
- jstree中文github文档
- apache加载模块的说明
- Matlab 形态学图像处理
- C++ STL/ (13) 常用遍历算法
- bzoj 4199: [Noi2015]品酒大会 后缀数组+并查集
- ImportError: cannot import name NUMPY_MKL
- 模块化和组件化的理解
- 【linux】chwon和chmod区别
- [python爬虫]使用Python爬取网易新闻
- Jquery实现无限级树状结构并动态添加增删改等编辑功能
- CodeForces 796A Buying A House
- C++中break、continue、return的区别
- Scala中的下划线到底有多少种应用场景?