[LOJ6198]谢特
description
给你一个字符串和一个数组\(w_i\),定义\(\mbox{LCP}(i,j)\)为\(i,j\)两个后缀的最长公共前缀。求\(\max_{i,j}\mbox{LCP}(i,j)+(w_i\ \mbox{xor}\ w_j)\)。
\(n \le 10^5\)
sol
首先,\(\mbox{LCP}(i,j)\)是后缀排序上的一段连续区间的\(\mbox{Height}\)最小值。
那么我们可以枚举这个最小值出现的位置,那么跨越这个位置的所有点对的\(\mbox{LCP}\)就确定了。
接下来我们只要考虑最大化\(w_i\ \mbox{xor}\ w_j\)。
显然可以用可持久化\(\mbox{Tire}\)树实现,复杂度是查询次数\(\times \log n\)的。
算法流程大致是这样的:先找到\(\mbox{Height}\)最小的位置\(p\),计算所有跨越\(p\)的点对的答案。枚举分割点两侧\(size\)较小的一侧,在可持久化\(\mbox{Tire}\)树上查询它和另一侧异或的最大值。接着两侧被完全割裂开,可以分别递归下去处理。
考虑这个东西的时间复杂度。可以发现这就是一个倒着做的启发式合并,所以复杂度就是\(O(n\log^2n)\)。
当然也可以按\(\mbox{Height}\)从大到小启发式合并,复杂度是一样的。
code
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N = 1e5+5;
struct node{int ch[2],sz;}tr[N*35];
int n,t[N],x[N],y[N],SA[N],Rank[N],Height[N],st[19][N],lg[N],rt[N],tot,w[N],ans;
char s[N];
bool cmp(int i,int j,int k){
return y[i]==y[j]&&y[i+k]==y[j+k];
}
void getSA(){
int m=30;
for (int i=1;i<=n;++i) ++t[x[i]=s[i]-'a'+1];
for (int i=1;i<=m;++i) t[i]+=t[i-1];
for (int i=n;i;--i) SA[t[x[i]]--]=i;
for (int k=1;k<=n;k<<=1){
int p=0;
for (int i=0;i<=m;++i) y[i]=0;
for (int i=n-k+1;i<=n;++i) y[++p]=i;
for (int i=1;i<=n;++i) if (SA[i]>k) y[++p]=SA[i]-k;
for (int i=0;i<=m;++i) t[i]=0;
for (int i=1;i<=n;++i) ++t[x[y[i]]];
for (int i=1;i<=m;++i) t[i]+=t[i-1];
for (int i=n;i;--i) SA[t[x[y[i]]]--]=y[i];
swap(x,y);x[SA[1]]=p=1;
for (int i=2;i<=n;++i) x[SA[i]]=cmp(SA[i],SA[i-1],k)?p:++p;
if (p>=n) break;m=p;
}
for (int i=1;i<=n;++i) Rank[SA[i]]=i;
for (int i=1,j=0;i<=n;++i){
if (j) --j;
while (s[i+j]==s[SA[Rank[i]-1]+j]) ++j;
Height[Rank[i]]=j;st[0][i]=i;
}
for (int i=2;i<=n;++i) lg[i]=lg[i>>1]+1;
for (int j=1;j<=lg[n];++j)
for (int i=1;i+(1<<j)-1<=n;++i)
if (Height[st[j-1][i]]<Height[st[j-1][i+(1<<j-1)]])
st[j][i]=st[j-1][i];
else st[j][i]=st[j-1][i+(1<<j-1)];
}
int cal(int l,int r){
int k=lg[r-l+1];
if (Height[st[k][l]]<Height[st[k][r-(1<<k)+1]]) return st[k][l];
return st[k][r-(1<<k)+1];
}
void modify(int &x,int p,int dep){
tr[++tot]=tr[x];++tr[x=tot].sz;
if (!~dep) return;
modify(tr[x].ch[(p>>dep)&1],p,dep-1);
}
int query(int x,int y,int p,int dep){
if (!~dep) return 0;
int c=(p>>dep)&1;c^=1;
if (tr[tr[x].ch[c]].sz-tr[tr[y].ch[c]].sz) return query(tr[x].ch[c],tr[y].ch[c],p,dep-1)|(1<<dep);
c^=1;return query(tr[x].ch[c],tr[y].ch[c],p,dep-1);
}
void solve(int l,int r){
if (l==r) return;int mid=cal(l+1,r)-1;
if (mid-l+1<=r-mid){
for (int i=l;i<=mid;++i)
ans=max(ans,Height[mid+1]+query(rt[r],rt[mid],w[SA[i]],18));
}else{
for (int i=mid+1;i<=r;++i)
ans=max(ans,Height[mid+1]+query(rt[mid],rt[l-1],w[SA[i]],18));
}
solve(l,mid);solve(mid+1,r);
}
int main(){
n=gi();scanf("%s",s+1);getSA();
for (int i=1;i<=n;++i) w[i]=gi();
for (int i=1;i<=n;++i) modify(rt[i]=rt[i-1],w[SA[i]],18);
solve(1,n);printf("%d\n",ans);return 0;
}
[LOJ6198]谢特的更多相关文章
- loj6198谢特 后缀数组+并查集+Trie
先把问题放在后缀数组上考虑 已知两个数组a b,求min(a[i],...,a[j])+(b[i]^b[j])的最大值 套路题 初始每个点都是一个小连通块 把a按从大到小的顺序加入,计算当前加入边作为 ...
- 【loj6198】谢特
Portal -->loj6198 Solution (为什么感觉loj上面这几道后缀数组的题..套路都是一样的啊qwq) 同样也是..考虑某个区间\(height[i]\)的最小值的贡 ...
- 谢欣伦 - OpenDev原创教程 - 媒体开发库libMedia
libMedia是一个免费的简单的媒体开发库,其中的接口类与函数大都以小写的x打头,来源于我的姓氏首字母(谢欣伦). 下载 OpenDev for VS2012 libMedia提供四大功能,一是视频 ...
- 谢欣伦 - OpenDev原创教程 - 蓝牙设备查找类CxBthRadio & CxBthRadioFind
这是一个精练的蓝牙设备查找类,类名.函数名和变量名均采用匈牙利命名法.小写的x代表我的姓氏首字母(谢欣伦),个人习惯而已,如有雷同,纯属巧合. CxBthRadioFind的使用如下: void CU ...
- 谢欣伦 - OpenDev原创教程 - 服务端套接字类CxServerSocket
这是一个精练的服务端套接字类,类名.函数名和变量名均采用匈牙利命名法.小写的x代表我的姓氏首字母(谢欣伦),个人习惯而已,如有雷同,纯属巧合. CxServerSocket的使用如下(以某个叫做CSo ...
- 谢欣伦 - OpenDev原创教程 - 蓝牙设备查找类CxBthRemoteDeviceFind
这是一个精练的蓝牙设备查找类,类名.函数名和变量名均采用匈牙利命名法.小写的x代表我的姓氏首字母(谢欣伦),个人习惯而已,如有雷同,纯属巧合. CxBthRemoteDeviceFind的使用如下: ...
- 谢欣伦 - OpenDev原创教程 - 无连接套接字类CxUdpSocket
这是一个精练的无连接套接字类,类名.函数名和变量名均采用匈牙利命名法.小写的x代表我的姓氏首字母(谢欣伦),个人习惯而已,如有雷同,纯属巧合. CxUdpSocket的使用如下(以某个叫做CSomeC ...
- 谢欣伦 - OpenDev原创教程 - 串口类CxSerial
这是一个精练的串口类,类名.函数名和变量名均采用匈牙利命名法.小写的x代表我的姓氏首字母(谢欣伦),个人习惯而已,如有雷同,纯属巧合. 串口类CxSerial的使用如下(以某个叫做CSomeClass ...
- 谢欣伦 - OpenDev原创教程 - 客户端套接字类CxClientSocket
这是一个精练的客户端套接字类,类名.函数名和变量名均采用匈牙利命名法.小写的x代表我的姓氏首字母(谢欣伦),个人习惯而已,如有雷同,纯属巧合. CxClientSocket的使用如下(以某个叫做CSo ...
随机推荐
- EF Code First 学习笔记:表映射(转)
多个实体映射到一张表 Code First允许将多个实体映射到同一张表上,实体必须遵循如下规则: 实体必须是一对一关系 实体必须共享一个公共键 观察下面两个实体: public class Per ...
- dubbo熔断,限流,服务降级
1 写在前面 1.1 名词解释 consumer表示服务调用方 provider标示服务提供方,dubbo里面一般就这么讲. 下面的A调用B服务,一般是泛指调用B服务里面的一个接口. 1.2 拓扑图 ...
- RedHot 不能联网 ifconfig命令只显示lo 不显示eth0的解决方法!
1.修改eth0 具体步骤如下 vi /etc/sysconfig/network-scripts/ifcfg-eth0, i,进入insert编辑模式,具体参数设置如下: DEVICE=eth0 B ...
- JS localStorage 存储变量
if(!window.localStorage && /MSIE/.test(navigator.userAgent)){ if(!window.UserData) { window. ...
- install ros-indigo-pcl-ros
CMake Warning at /opt/ros/indigo/share/catkin/cmake/catkinConfig.cmake: (find_package): Could not fi ...
- devdocs
https://devdocs.io/ docker run --rm -d --name devdocs -p 9292:9292 devdocs/devdocs
- 10046event vs 10053event做了哪些操作
10046 event 告诉我们oracle做了哪些事情,做的怎么样.10053 event 告诉我们oracle优化器怎么想的,再给出成本最低的最终的优化方案,考虑了哪些执行计划拿了哪些统计信息
- 从数组里随机获取N项
基础知识: 复制数组: (1)循环遍历复制(不推荐) var arry = [1,5,9,7], new_arry = [], n = 0, len = arry.length; for(;n< ...
- C++ lower_bound 和upper_bound
二分查找的函数有 3 个: 1.lower_bound(起始地址,结束地址,要查找的数值) 返回的是数值 第一个 出现的位置. 2.upper_bound(起始地址,结束地址,要查找的数值) 返回的是 ...
- 前端构建工具 Grunt 入门
之前也介绍过前端构建工具 Ant 和 Yeoman,其中 Yeoman 工具就包含了 Grunt 所以就不多说.那么与 Ant 相比 Grunt 有这么几个优点: Javascript 语法,相比 A ...