1 条题解
-
0
自动搬运
来自洛谷,原作者为

Farkas_W
醉后不知天在水,满船清梦压星河。搬运于
2025-08-24 21:52:08,当前版本为作者最后更新于2021-10-10 15:03:28,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
我们的模拟赛考了这道题,只会暴力的 30 分,考后看 std 才发现随机数据是怎么用的。(
另外其他题解也太长了吧)考虑 trie 树,因为是随机数据,显然两段长为 的字符串完全相同的概率是 ,所以取的要尽量大且不会爆空间。我们对于每一个位置,只存其后长为 的字符串即可(长度取 只可以得到 分),暴力将每一个位置存入时, 数组记录下最近到达这个位置的 , 数组记录上一个到达这个位置的字符串编号,每次更新 数组。
设以第 个位置开始(结束位置为 ) 的字符串为字符串 ,建立 trie树后, 表示与字符串 前 位完全相同的最近的字符串编号 (这里的最近指的是小于 的且最靠近 的编号)。
接着对 数组做一个前缀最大值, 表示从字符串 到字符串 中,最大的一对长为 的前缀相同的字符串编号中较小的。假如 ,满足
所以对于一个询问 ,假如 ,显然对于 ,这样区间 的答案就统计好了,接着继续统计询问 即可,可以令 ,就这样一段区间一段区间的向后跳。
时间复杂度为 ,开 后跑了 207ms,目前最优解。如果有什么疑问就看代码吧。
il int max(int x,int y){return x>=y?x:y;} const int N=5e5+5; int n,m,a[N*47],ch[N*47][2],cnt,last[N][48]; char c[N]; il void insert(int id) { int u=0; for(re i=id;i<=id+46;i++)//只更新长度47 { if(i>n)return;bool p=(c[i]=='1'); if(!ch[u][p])ch[u][p]=++cnt; u=ch[u][p];last[id][i-id+1]=a[u]; a[u]=id;//更新 } } signed main() { cin>>n>>m>>c+1; for(re i=1;i<=n;i++)insert(i); for(re i=2;i<=n;i++) for(re j=1;j<=47;j++) { last[i][j]=max(last[i][j],last[i-1][j]);//前缀最大值 if(!last[i-1][j])break;//表示前面没有前j位相同的字符串 } while(m--){ int l,r,ans=0;cin>>l>>r; for(re i=47;i&&l<=r;i--) if(last[r][i]>=l){ //有符合区间的字符串 ans+=(last[r][i]-l+1)*i;//统计答案 l=last[r][i]+1; } cout<<ans<<'\n'; } return 0; }
- 1
信息
- ID
- 1302
- 时间
- 2000ms
- 内存
- 250MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者