【BZOJ4199】品酒大会,后缀数组+并查集维护
2016-05-23 22:11
330 查看
Time:2016.05.23
Author:xiaoyimi
转载注明出处谢谢
传送门
题面及样例
思路:
求lcp相关问题上有类似于差异的地方,但不完全是,”差异“所求的是lcp长度总和,而这里要对每个长度的lcp统计出现次数,更加精细
以前单调栈的做法不能满足我们了,只能另辟蹊径
(一开始我想的是线段树啥的……)
考虑这样一种思路,一开始每个后缀都是一个独立的集合,对于每个height[i],它可以把两个后缀集合x,y合并,那么对ans[height[i]]的贡献是siz[x]*siz[y],可以对最大乘积的贡献是max(这两个集合最大值的乘积,最小值的乘积)
用并查集维护每个集合的左端点,右端点,最小值和最大值
之后的事情就比较容易了
注意:
对于0相似的情况,个数肯定是n*(n-1)/2,最大值肯定是max(次大值最大值,最小值次小值)
代码:
Author:xiaoyimi
转载注明出处谢谢
传送门
题面及样例
思路:
求lcp相关问题上有类似于差异的地方,但不完全是,”差异“所求的是lcp长度总和,而这里要对每个长度的lcp统计出现次数,更加精细
以前单调栈的做法不能满足我们了,只能另辟蹊径
(一开始我想的是线段树啥的……)
考虑这样一种思路,一开始每个后缀都是一个独立的集合,对于每个height[i],它可以把两个后缀集合x,y合并,那么对ans[height[i]]的贡献是siz[x]*siz[y],可以对最大乘积的贡献是max(这两个集合最大值的乘积,最小值的乘积)
用并查集维护每个集合的左端点,右端点,最小值和最大值
之后的事情就比较容易了
注意:
对于0相似的情况,个数肯定是n*(n-1)/2,最大值肯定是max(次大值最大值,最小值次小值)
代码:
#include<bits/stdc++.h> #define M 300004 #define LL long long using namespace std; int len; char s[M]; int minn[M],maxn[M],a[M],w[M],fa[M],sa[M],rank[M],cnt[M],tmp[M],id[M],L[M],R[M]; LL ans[2][M]; struct node { int data,id; bool operator <(const node other)const { return data>other.data; } }height[M]; int in() { char ch=getchar();int t=0;bool f=0; while (!isdigit(ch)) f=(ch=='-'),ch=getchar(); while (isdigit(ch)) t=(t<<1)+(t<<3)+ch-48,ch=getchar(); return f?-t:t; } void SA(int len,int up) { int *rk=rank,*t=tmp,d=1,p=0; for (int i=0;i<len;i++) cnt[rk[i]=w[i]]++; for (int i=1;i<up;i++) cnt[i]+=cnt[i-1]; for (int i=len-1;i>=0;i--) sa[--cnt[rk[i]]]=i; for (;;) { for (int i=len-d;i<len;i++) id[p++]=i; for (int i=0;i<len;i++) if (sa[i]>=d) id[p++]=sa[i]-d; for (int i=0;i<up;i++) cnt[i]=0; for (int i=0;i<len;i++) cnt[t[i]=rk[id[i]]]++; for (int i=1;i<up;i++) cnt[i]+=cnt[i-1]; for (int i=len-1;i>=0;i--) sa[--cnt[t[i]]]=id[i]; swap(t,rk); p=1; rk[sa[0]]=0; for (int i=0;i<len-1;i++) if (sa[i]+d<len&&sa[i+1]+d<len&&t[sa[i]]==t[sa[i+1]]&&t[sa[i]+d]==t[sa[i+1]+d]) rk[sa[i+1]]=p-1; else rk[sa[i+1]]=p++; if (p==len) break; d<<=1;up=p;p=0; } } void Height(int len) { for (int i=1;i<=len;i++) rank[sa[i]]=i; int k=0,x; for (int i=0;i<len;i++) { k=max(k-1,0); x=sa[rank[i]-1]; while (w[i+k]==w[x+k]) k++; height[rank[i]]=(node){k,rank[i]}; } } int find(int x){return fa[x]=(x!=fa[x]?find(fa[x]):fa[x]);} main() { len=in(); scanf("%s",s); memset(ans[1],-127,sizeof(ans[1])); for (int i=0;i<len;i++) a[i]=in(); for (int i=0;i<len;i++) w[i]=s[i]-'a'+1; SA(len+1,28); Height(len); sort(height+1,height+len+1); for (int i=1;i<=len;i++) fa[i]=i,L[i]=i,R[i]=i,minn[i]=maxn[i]=a[sa[i]]; for (int i=1;i<=len;i++) if (!height[i].data) break; else { int p=find(height[i].id),q=find(height[i].id-1); ans[0][height[i].data]+=(LL)(R[p]-L[p]+1)*(R[q]-L[q]+1); ans[1][height[i].data]=max((LL)minn[p]*minn[q],max(ans[1][height[i].data],(LL)maxn[p]*maxn[q])); fa[p]=q; L[q]=min(L[p],L[q]); R[q]=max(R[p],R[q]); minn[q]=min(minn[p],minn[q]); maxn[q]=max(maxn[p],maxn[q]); } for (int i=len-1;i>=1;i--) ans[0][i]+=ans[0][i+1], ans[1][i]=max(ans[1][i],ans[1][i+1]); sort(a,a+len); printf("%lld %lld\n",(LL)len*(len-1)>>1,max((LL)a[1]*a[0],(LL)a[len-2]*a[len-1])); for (int i=1;i<len;i++) printf("%lld %lld\n",ans[0][i],!ans[0][i]?0:ans[1][i]); }
相关文章推荐
- 高并发金融应用架构优化与平台创新
- shell通配符
- 阿里云服务器-百度开放云域名-WordPress网站部署踩的坑
- kalilinux 更新
- apache 重定向
- go语言exec包调用shell命令
- Linuxc编程的常用关键字 register ,static ,extern ,const ,typedef
- Linux堆内存管理深入分析
- Kali Linux 教程 之 Kali Linux 更新源
- Centos 6.5 本地局域网基于FTP搭建YUM
- linux环境下GDB与core dump调试程序方法
- CentOS 7之Systemd详解之服务单元设置system.service
- 【BZOJ3238】差异,后缀数组+单调栈维护height
- Linux:内核模块实现替换系统调用的简单例子
- Linux下配login.sql,并在.bash_profile中配置SQLPATH
- cookie案例-显示用户上次访问网站的时间
- 从头开始学算法--NUM operation in MIX
- bash 数组
- 《linux内核设计与分析》内核模块编程
- centos6.5 运行可执行.sh