P7046-「MCOI-03」诗韵【SAM,倍增,树状数组】

1|0正题

题目链接:https://www.luogu.com.cn/problem/P7046


1|1题目大意

给出一个长度为 n 的字符串,然后 m 次把它的一个子串加入集合。如果一个字符串在这个集合中作为字符串的后缀出现次数大于 k 那么这个字符串就会被计入贡献。
每次求计入贡献的字符串数和最长长度。

1n,m5×105,0k<n


1|2解题思路

考虑在parents树上如果能定位到一个节点的字符串那么它的后缀就是它到根的路径。

但是可能定位不到根,一种暴力的做法是每条边上建一个线段树然后暴力改。但是这样很麻烦可以考虑让每个询问一定能定位到一个节点。

我们直接建好树然后每次把询问倍增挂到对应的边上用set储存,然后再重新建一棵包含每个节点的树。

那么现在问题就变为了统计子树权值大于 k 的节点了,因为每个点到根的路径上满足条件的边一定是一段后缀,而每个节点最多统计一次,所以我们直接每次倍增找到最上面的没有统计的节点用树状数组+dfs 序判断是否合法就好了。

时间复杂度:O(nlogn)(默认 n,m 同级)


1|3code

#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; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/15321287.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(29)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示