P7046-「MCOI-03」诗韵【SAM,倍增,树状数组】
正题
题目链接:https://www.luogu.com.cn/problem/P7046
题目大意
给出一个长度为 \(n\) 的字符串,然后 \(m\) 次把它的一个子串加入集合。如果一个字符串在这个集合中作为字符串的后缀出现次数大于 \(k\) 那么这个字符串就会被计入贡献。
每次求计入贡献的字符串数和最长长度。
\(1\leq n,m\leq 5\times 10^5,0\leq k<n\)。
解题思路
考虑在parents树上如果能定位到一个节点的字符串那么它的后缀就是它到根的路径。
但是可能定位不到根,一种暴力的做法是每条边上建一个线段树然后暴力改。但是这样很麻烦可以考虑让每个询问一定能定位到一个节点。
我们直接建好树然后每次把询问倍增挂到对应的边上用set储存,然后再重新建一棵包含每个节点的树。
那么现在问题就变为了统计子树权值大于 \(k\) 的节点了,因为每个点到根的路径上满足条件的边一定是一段后缀,而每个节点最多统计一次,所以我们直接每次倍增找到最上面的没有统计的节点用树状数组+\(dfs\) 序判断是否合法就好了。
时间复杂度:\(O(n\log n)\)(默认 \(n,m\) 同级)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#define lowbit(x) (x&-x)
using namespace std;
const int M=1e6+10,N=2e6+10,T=21;
struct node{
int to,next;
}a[N];
int n,m,k,last,cnt,tot,ans2;long long ans1;
int ch[M][26],len[N],fa[N],L[M],R[M],p[M],las[M];
int ls[N],v[N],pos[N],f[N][T],rfn[N],ed[N],dos[M];
set<int> ct[M];vector<int> q[M];char s[M];
set<int>::iterator it;
void Insert(int c){
int p=last,np=last=++cnt;len[np]=len[p]+1;
for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
if(!p)fa[np]=1;
else{
int q=ch[p][c];
if(len[q]==len[p]+1)fa[np]=q;
else{
int nq=++cnt;len[nq]=len[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[nq]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;
}
}
return;
}
void work(int p,int l,int r){
int x=pos[r];
for(int i=T-1;i>=0;i--)
if(len[f[x][i]]>=r-l+1)x=f[x][i];
if(ct[x].count(r-l+1))return;
ct[x].insert(r-l+1);q[x].push_back(p);
return;
}
void addl(int x,int y){
a[++tot].to=y;
a[tot].next=ls[x];
ls[x]=tot;return;
}
bool cmp(int x,int y)
{return R[x]-L[x]+1<R[y]-L[y]+1;}
bool cMp(int x,int y)
{return len[x]<len[y];}
void dfs(int x){
rfn[x]=++cnt;
for(int i=ls[x];i;i=a[i].next){
int y=a[i].to;
f[y][0]=x;dfs(a[i].to);
}
ed[x]=cnt;return;
}
struct TreeBinary{
int t[N];
void Change(int x,int val){
while(x<=n){
t[x]+=val;
x+=lowbit(x);
}
return;
}
int Ask(int x){
int ans=0;
while(x){
ans+=t[x];
x-=lowbit(x);
}
return ans;
}
}B;
int main()
{
scanf("%d%d%d",&n,&m,&k);
scanf("%s",s+1);cnt=last=1;
for(int i=1;i<=n;i++)Insert(s[i]-'a'),pos[i]=last;
for(int i=1;i<=cnt;i++)f[i][0]=fa[i];
for(int j=1;j<T;j++)
for(int i=1;i<=cnt;i++)
f[i][j]=f[f[i][j-1]][j-1];
for(int i=1;i<=m;i++){
scanf("%d%d",&L[i],&R[i]);
work(i,L[i],R[i]);
}
for(int i=1;i<=cnt;i++)p[i]=i,ct[i].insert(len[i]);
sort(p+1,p+1+cnt,cMp);
int pnt=cnt;cnt=1;las[1]=1;
for(int i=2;i<=pnt;i++){
int x=p[i];
sort(q[x].begin(),q[x].end(),cmp);
int z=0;las[x]=las[fa[x]];
it=ct[x].begin();
while(1){
++cnt;addl(las[x],cnt);las[x]=cnt;
int W=*it;len[cnt]=*it;
while(z<q[x].size()&&R[q[x][z]]-L[q[x][z]]+1<=W)
dos[q[x][z]]=cnt,z++;
it++;if(it==ct[x].end())break;
}
}
n=cnt;cnt=0;dfs(1);v[0]=1;len[0]=-1;
for(int j=1;j<T;j++)
for(int i=1;i<=n;i++)
f[i][j]=f[f[i][j-1]][j-1];
for(int i=1;i<=m;i++){
int p=dos[i];
if(!p){printf("%lld %d\n",ans1,ans2);continue;}
B.Change(rfn[p],1);
if(v[p]){printf("%lld %d\n",ans1,ans2);continue;}
while(!v[p]){
int x=p;
for(int j=T-1;j>=0;j--)
if(!v[f[x][j]])x=f[x][j];
int w=B.Ask(ed[x])-B.Ask(rfn[x]-1);
if(w>k){
v[x]=1;
ans1+=len[x]-len[f[x][0]];
ans2=max(ans2,len[x]);
}
else break;
}
printf("%lld %d\n",ans1,ans2);
}
return 0;
}
P7046-「MCOI-03」诗韵【SAM,倍增,树状数组】的更多相关文章
- 【bzoj3488】[ONTAK2010]Highways DFS序+树上倍增+树状数组
题目描述 一棵n个点的树,给定m条路径,q次询问包含一条路径的给定路径的个数+1 输入 The first line of input contains a single integer N(1< ...
- loj#2059. 「TJOI / HEOI2016」字符串 sam+线段树合并+倍增
题意:给你一个子串,m次询问,每次给你abcd,问你子串sa-b的所有子串和子串sc-d的最长公共前缀是多长 题解:首先要求两个子串的最长公共前缀就是把反过来插入变成最长公共后缀,两个节点在paren ...
- LOJ #2719. 「NOI2018」冒泡排序(组合数 + 树状数组)
题意 给你一个长为 \(n\) 的排列 \(p\) ,问你有多少个等长的排列满足 字典序比 \(p\) 大 : 它进行冒泡排序所需要交换的次数可以取到下界,也就是令第 \(i\) 个数为 \(a_i\ ...
- 「模拟赛20180307」三元组 exclaim 枚举+树状数组
题目描述 给定 \(n,k\) ,求有多少个三元组 \((a,b,c)\) 满足 \(1≤a≤b≤c≤n\)且\(a + b^2 ≡ c^3\ (mod\ k)\). 输入 多组数据,第一行数据组数\ ...
- LG5200 「USACO2019JAN」Sleepy Cow Sorting 树状数组
\(\mathrm{Sleepy Cow Sorting}\) 问题描述 LG5200 题解 树状数组. 设\(c[i]\)代表\([1,i]\)中归位数. 显然最终的目的是将整个序列排序为一个上升序 ...
- LOJ6041 SAM+set+树状数组
首先对于原串建$SAM$,我们可以发先在一个点$i$的$right$集合里的点的相似度就是$len[i]$,于是可以将$SAM$的$right$集合通过$set$来启发式合并,每次加入新的点对$(i, ...
- 「ZJOI2017」树状数组(二维线段树)
「ZJOI2017」树状数组(二维线段树) 吉老师的题目真是难想... 代码中求的是 \(\sum_{i=l-1}^{r-1}a_i\),而实际求的是 \(\sum_{i=l}^{r}a_i\),所以 ...
- loj #535. 「LibreOJ Round #6」花火 树状数组求逆序对+主席树二维数点+整体二分
$ \color{#0066ff}{ 题目描述 }$ 「Hanabi, hanabi--」 一听说祭典上没有烟火,Karen 一脸沮丧. 「有的哦-- 虽然比不上大型烟花就是了.」 还好 Shinob ...
- 「UNR#1」奇怪的线段树
「UNR#1」奇怪的线段树 一道好题,感觉解法非常自然. 首先我们只需要考虑一次染色最下面被包含的那些区间,因为把无解判掉以后只要染了一个节点,它的祖先也一定被染了.然后发现一次染色最下面的那些区间一 ...
随机推荐
- 【Openxml】将Openxml的椭圆弧线arcTo转为Svg的椭圆弧线
本文将介绍如何将OpenXml的actTo转为Svg的弧线(a) OpenXml的artTo 首先下面是一段OpenXml的arcTo弧线 <arcTo wR="152403" ...
- SQL查询对分数进行排名
编写SQL查询以对分数进行排名. 如果两个分数之间存在平局,则两者应具有相同的排名. 请注意,在平局之后,下一个排名数应该是下一个连续的整数值. 换句话说,等级之间不应该存在"漏洞" ...
- Java知识图谱(附:阿里Java学习计划)
摘要: 本文主要描绘了Java基础学习过程,给出Java知识结构图,以及阿里Java岗学习计划,对Java学习爱好者.准备及将要从事Java开发方面的同学大有裨益. 温馨提示: 由于C ...
- Struts中整合的强大Ognl学习(一)
测试使用了一个JavaBean的User,User中的Address单独封装再形成了一个JavaBean: 为了测试静态方法和静态变量调用,写了一个Util方法: 因为测试Ognl功能过多所以直接使用 ...
- 转:自增(自减)在Java与C中的区别
转自:http://seiyatime.blog.sohu.com/84358295.html 话说昨日面试,在笔试的25个选择题中,涉及自增自减不止一两题,以前在开发过程中并没太在意这方面的问题,也 ...
- 前端性能优化(四)——网页加载更快的N种方式
网站前端的用户体验,决定了用户是否想要继续使用网站以及网站的其他功能,网站的用户体验佳,可留住更多的用户.除此之外,前端优化得好,还可以为企业节约成本.那么我们应该如何对我们前端的页面进行性能优化呢? ...
- Insights直播预告 | 多媒体管线服务,助您轻松进入“技术流”创新阵地
[导读] 随着各类音视频移动应用快速发展,短视频.线上直播等娱乐方式逐渐为大众所喜爱.优质的视听效果和交互体验,往往能吸引更多的用户.多媒体管线服务作为一个轻量级的多媒体开发框架,其跨平台.高性能的多 ...
- linux常用查询命令
1 **系统** 2 # uname -a # 查看内核/操作系统/CPU信息 3 # head -n 1 /etc/issue # 查看操作系统版本 4 # cat /proc/cpuinfo # ...
- 自定义组件 v-model 的使用
关于自定义组件如何使用 v-model,本章直讲如何使用: 一. $emit('input', params) // 父组件中 <template> <article> {{f ...
- 理解ASP.NET Core - [04] Host
注:本文隶属于<理解ASP.NET Core>系列文章,请查看置顶博客或点击此处查看全文目录 本文会涉及部分 Host 相关的源码,并会附上 github 源码地址,不过为了降低篇幅,我会 ...