这两道题好像啊

贡献一种使用SAM和ACAM草两道题的方法

下面假装有 \(O(\sum |S|=m)=O(n)\)。

你看看,这CF换过多少个出题人啦?换汤不换药啦!其实这两道题是同一个人出的

CF587F

根号分治。把长度大于 \(B\) 的串全部拿出来,有 \(\frac{m}{B}\) 个串。

然后对于这些串,每个串建一个 SAM,然后把 \(n\) 个串全部丢进来匹配。只需要对 SAM 的每一个节点维护 endpos 的大小即可。复杂度 \(O(\frac{nm}{B})\)。

对于剩下的部分,查询一个串在一个区间中出现过多少次。首先将询问差分成前缀和。

然后从左往右扫每一个串。能够被一个串贡献到的后缀一定在 fail 树上该节点的子树中。

查询的时候暴力调一遍 trie 的祖先就好了。

需要 \(O(n)\) 次区间加和 \(O(qB)\) 次单点查询。用差分转化成 \(O(n)\) 次单点修改和 \(O(qB)\) 次前缀和。使用分块维护即可。(\(O(\sqrt{n})-O(1)\))

取 \(B=\sqrt{\frac{nm}{q}}\) 即可得到 \(O(\sqrt{nmq}+n\sqrt{n})\) 的优秀复杂度。

#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
typedef unsigned ui;
const ui SZ=512,M=1e5+5;
ui n,m,q,B,num,id[SZ],t[M],l[M],r[M],k[M],cnt[SZ][M];ui tot(1),fail[M],trans[M][26];
char S[M<<1],*s[M];ui len[M];std::vector<ui>Q[M];ui ans[M];bool vis[M];
namespace SAM{
ui tot,lst,f[M<<1],sz[M<<1],len[M<<1],trans[M<<1][26];
inline void Insert(const ui&s){
ui q,p,nq,np;
p=lst;len[np=lst=++tot]=len[p]+1;
while(p&&!trans[p][s])trans[p][s]=np,p=f[p];
if(!p)f[np]=1;
else{
if(len[q=trans[p][s]]==len[p]+1)f[np]=q;
else{
len[nq=++tot]=len[p]+1;f[nq]=f[q];f[q]=f[np]=nq;
memcpy(trans[nq],trans[q],104);
while(p&&trans[p][s]==q)trans[p][s]=nq,p=f[p];
}
}
}
inline ui Match(char*s,const ui&n){
ui u(1);for(ui i=0;i<n;++i)u=trans[u][s[i]-97];return sz[u];
}
inline void Clear(){
while(tot)memset(trans[tot],0,104),f[tot]=sz[tot]=len[tot]=0,--tot;
tot=lst=1;
}
inline void Build(char*s,const ui&n){
static ui CB[M],id[M<<1];
for(ui i=0;i<n;++i)Insert(s[i]-97),++sz[lst];
for(ui i=1;i<=n;++i)CB[i]=0;
for(ui u=1;u<=tot;++u)++CB[len[u]];
for(ui i=1;i<=n;++i)CB[i]+=CB[i-1];
for(ui u=tot;u>=1;--u)id[CB[len[u]]--]=u;
for(ui i=tot;i>1;--i)sz[f[id[i]]]+=sz[id[i]];
}
}
struct Block{
ui n,B,P,S[SZ],L[M],R[M],s[M],pos[M];
inline void init(const ui&m){
B=ceil(sqrt(n=m));
for(ui i=1;i<=n;++i){
pos[i]=(i-1)/B+1;L[i]=(pos[i]-1)*B+1;R[i]=pos[i]*B;
if(R[i]>n)R[i]=n;
}
P=pos[n];
}
inline void Add(const ui&x){
if(x>n)return;
for(ui i=x;i<=R[x];++i)++s[i];
for(ui i=pos[x];i<=P;++i)++S[i];
}
inline void Del(const ui&x){
if(x>n)return;
for(ui i=x;i<=R[x];++i)--s[i];
for(ui i=pos[x];i<=P;++i)--S[i];
}
inline ui Qry(const ui&x){
return s[x]+S[pos[x]-1];
}
}block;
struct Fail{
ui cnt,h[M];
ui dfc,L[M],R[M],dfn[M];
struct Edge{
ui v,nx;
}e[M<<1];
inline void Add(const ui&u,const ui&v){
e[++cnt]=(Edge){v,h[u]};h[u]=cnt;
}
inline void init(const ui&u){
dfn[u]=++dfc;L[u]=dfc;for(ui E=h[u];E;E=e[E].nx)init(e[E].v);R[u]=dfc;
}
inline void Mdf(const ui&u){
block.Add(L[u]);block.Del(R[u]+1);
}
inline ui Qry(const ui&u){
return block.Qry(dfn[u]);
}
}failtree;
inline void Insert(char*s,const ui&n){
ui u(1);
for(ui i=0;i<n;++i){
const ui&c=s[i]-97;
if(!trans[u][c])trans[u][c]=++tot;
u=trans[u][c];
}
}
inline ui Qry(char*s,const ui&n){
ui u(1),ans(0);
for(ui i=0;i<n;++i)ans+=failtree.Qry(u=trans[u][s[i]-97]);
return ans;
}
inline void Build(){
static ui L,R,q[M];L=1;
for(ui c=0;c<26;++c){
if(trans[1][c])fail[trans[1][c]]=1,q[++R]=trans[1][c];
else trans[1][c]=1;
}
while(L<=R){
const ui&u=q[L++];
for(ui c=0;c<26;++c){
if(trans[u][c])fail[trans[u][c]]=trans[fail[u]][c],q[++R]=trans[u][c];
else trans[u][c]=trans[fail[u]][c];
}
}
}
inline void init(){
B=ceil(sqrt(1.*n*m/q));{
ui cnt(0);
for(ui i=1;i<=n;++i)cnt+=len[i]>=B;
if(cnt>=SZ)B=ceil(sqrt(m));
}
for(ui i=1;i<=n;++i)if(len[i]>=B)id[++num]=i;
for(ui i=1;i<=num;++i){
SAM::Clear();SAM::Build(s[id[i]],len[id[i]]);t[id[i]]=i;
for(ui j=1;j<=n;++j)cnt[i][j]=cnt[i][j-1]+SAM::Match(s[j],len[j]);
}
for(ui i=1;i<=n;++i)Insert(s[i],len[i]);Build();block.init(tot);
for(ui u=2;u<=tot;++u)failtree.Add(fail[u],u);failtree.init(1);
}
inline void Solve(){
for(ui i=0;i<=n;++i){
if(i){
ui u(1);
for(ui k(0);k<len[i];++k)u=trans[u][s[i][k]-97];failtree.Mdf(u);
}
for(ui&id:Q[i])if(len[k[id]]<B){
if(vis[id])ans[id]+=Qry(s[k[id]],len[k[id]]);
else ans[id]-=Qry(s[k[id]],len[k[id]]),vis[id]=true;
}
else ans[id]=cnt[t[k[id]]][r[id]]-cnt[t[k[id]]][l[id]-1];
}
}
signed main(){
scanf("%u%u",&n,&q);s[0]=S;
for(ui i=1;i<=n;++i){
scanf("%s",s[i]=s[i-1]+len[i-1]);len[i]=strlen(s[i]);m+=len[i];
}
for(ui i=1;i<=q;++i){
scanf("%u%u%u",l+i,r+i,k+i);
Q[l[i]-1].push_back(i);Q[r[i]].push_back(i);
}
init();Solve();
for(ui i=1;i<=q;++i)printf("%u\n",ans[i]);
}

CF547E

直接使用 SAM 套线段树!!1大炮打蚊子

把所有串插到广义 SAM 里面,然后直接建立线段树。

提前处理每个串匹配到的位置。然后处理一个 endpos 的大小就好了。(其实是前一道题第一部分变成多串)

先建出广义 SAM,然后把每个串丢进去匹配,在路径上修改线段树,最后在 parent tree 上线段树合并即可。优秀的 \(O(n+(m+q)\log m)\)。

#include<cstring>
#include<cstdio>
typedef unsigned ui;
const ui M=2e5+5;
ui n,m,cnt,tot(1),sam(1),lst[M],trie[M][26];
ui f[M<<1],len[M<<1],root[M<<1],trans[M<<1][26];
char S[M],*s[M];ui Len[M],pos[M];
struct Node{
ui L,R,sum;
}t[M*30];
inline void Mdf(ui&u,const ui&x,const ui&L=1,const ui&R=n){
if(!u)u=++cnt;++t[u].sum;
if(L<R){
const ui mid=L+R>>1;
if(x<=mid)Mdf(t[u].L,x,L,mid);
else Mdf(t[u].R,x,mid+1,R);
}
}
inline ui Qry(const ui&u,const ui&l,const ui&r,const ui&L=1,const ui&R=n){
if(!u||l>R||L>r)return 0;
if(l<=L&&R<=r)return t[u].sum;
const ui mid=L+R>>1;return Qry(t[u].L,l,r,L,mid)+Qry(t[u].R,l,r,mid+1,R);
}
inline ui Merge(const ui&q,const ui&p){
if(!q||!p)return q|p;
const ui u=++cnt;t[u].sum=t[q].sum+t[p].sum;
t[u].L=Merge(t[q].L,t[p].L);t[u].R=Merge(t[q].R,t[p].R);
return u;
}
inline ui Insert(const ui&lst,const ui&s){
ui q,p,nq,np;
p=lst;len[np=++sam]=len[p]+1;
while(p&&!trans[p][s])trans[p][s]=np,p=f[p];
if(!p)f[np]=1;
else{
if(len[q=trans[p][s]]==len[p]+1)f[np]=q;
else{
len[nq=++sam]=len[p]+1;f[nq]=f[q];f[np]=f[q]=nq;
memcpy(trans[nq],trans[q],104);
while(p&&trans[p][s]==q)trans[p][s]=nq,p=f[p];
}
}
return np;
}
inline void Insert(char*s,const ui&n){
ui u(1);
for(ui i=0;i<n;++i){
const ui&c=s[i]-97;
if(!trie[u][c])trie[u][c]=++tot;
u=trie[u][c];
}
}
inline ui Mdf(char*s,const ui&n,const ui&id){
ui u(1);
for(ui i=0;i<n;++i)Mdf(root[u=trans[u][s[i]-97]],id);
return u;
}
inline void Build(){
static ui L,R,q[M],CB[M],id[M<<1];q[L=R=1]=1;lst[1]=1;
while(L<=R){
const ui&u=q[L++];
for(ui c=0;c<26;++c)if(ui v=trie[u][c]){
lst[v]=Insert(lst[u],c);q[++R]=v;
}
}
for(ui i=1;i<=n;++i)pos[i]=Mdf(s[i],Len[i],i);
for(ui u=1;u<=sam;++u)++CB[len[u]];
for(ui i=1;i<=tot;++i)CB[i]+=CB[i-1];
for(ui u=sam;u>=1;--u)id[CB[len[u]]--]=u;
for(ui i=sam;i>1;--i)root[f[id[i]]]=Merge(root[f[id[i]]],root[id[i]]);
}
signed main(){
scanf("%u%u",&n,&m);s[0]=S;
for(ui i=1;i<=n;++i){
scanf("%s",s[i]=s[i-1]+Len[i-1]);Len[i]=strlen(s[i]);Insert(s[i],Len[i]);
}
Build();
for(ui i=1;i<=m;++i){
ui l,r,k;scanf("%u%u%u",&l,&r,&k);
printf("%u\n",Qry(root[pos[k]],l,r));
}
}

CF587F&CF547E题解的更多相关文章

  1. 【CF587F】Duff is Mad AC自动机+分块

    [CF587F]Duff is Mad 题意:给出n个串$s_1,s_2..s_n$,有q组询问,每次给出l,r,k,问你编号在[l,r]中的所有串在$s_k$中出现了多少次. $\sum|s_i|, ...

  2. 2016 华南师大ACM校赛 SCNUCPC 非官方题解

    我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...

  3. noip2016十连测题解

    以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...

  4. BZOJ-2561-最小生成树 题解(最小割)

    2561: 最小生成树(题解) Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1628  Solved: 786 传送门:http://www.lyd ...

  5. Codeforces Round #353 (Div. 2) ABCDE 题解 python

    Problems     # Name     A Infinite Sequence standard input/output 1 s, 256 MB    x3509 B Restoring P ...

  6. 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解

    题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...

  7. 2016ACM青岛区域赛题解

    A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Jav ...

  8. poj1399 hoj1037 Direct Visibility 题解 (宽搜)

    http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...

  9. 网络流n题 题解

    学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...

随机推荐

  1. JFrame实现圆角窗体

    感谢大佬:https://blog.csdn.net/Mr_Pang/article/details/47808299?utm_source=blogxgwz0 注:使用AWTUtilities类跨平 ...

  2. nginx启动失败:Redirecting to /bin/systemctl start nginx.service Failed to start nginx.service: Unit not found.

    解决方法: 是因为nginx没有有添加到系统服务,手动手动添加一个即可. 在 /etc/init.d/下创建名为nginx的启动脚本即可,内容如下: #!/bin/bash # # chkconfig ...

  3. Net6 DI源码分析Part4 CallSiteFactory ServiceCallSite

    Net6 CallSiteFactory ServiceCallSite, CallSiteChain abstract class ServiceCallSite ServiceCallSite是个 ...

  4. CSS解决父级边框坍塌的问题

    1. 浮动元素后面增加空的div 首先在父级标签内添加如下<div>标签 <div id="clear"></div> 然后在CSS中对该标签进 ...

  5. postman中用当前时间戳做请求的入参

    用postman做接口测试的,有些接口需要带上当前时间的时间戳作为请求的入参,postman支持这种功能吗? 答案是肯定的. 文中有使用时间戳的两种方法和postman常用的预定义变量. 例子中接口的 ...

  6. 《PHP程序员面试笔试宝典》——如何巧妙地回答面试官的问题?

    如何巧妙地回答面试官的问题? 本文摘自<PHP程序员面试笔试宝典> 所谓"来者不善,善者不来",程序员面试中,求职者不可避免地需要回答面试官各种"刁钻&quo ...

  7. Solution -「CF 1370F2」The Hidden Pair (Hard Version)

    \(\mathcal{Description}\)   Link (hard) & Link (easy).   这是一道交互题.   给定一棵 \(n\) 个结点的树,其中有两个是特殊结点. ...

  8. Large Sacle Distributed Deep Networks

    本文是谷歌发表在NeurIPS 2012上的一篇论文,主要讨论了在几万个CPU节点上训练大规模深度网络的问题,并提出了一个名为DistBelief的软件框架.在该框架下实现了两种大规模分布式训练算法: ...

  9. Dubbo基础一之实战初体验

    本以为写这个小作文没什么难度的,可是好像并不是.前段时间重心放在驾考科目二,就想着小作文科二考过了再写也不是事,因为都实战过了.今天想着写却发现脑袋里啥都想不起来了,得翻项目和笔记回忆一下.所以还是那 ...

  10. ServiceStack.Redis的源码分析(连接与连接池)

    前几天在生产环境上redis创建连接方面的故障,分析过程中对ServiceStack.Redis的连接创建和连接池机制有了进一步了解.问题分析结束后,通过此文系统的将学习到的知识点整理出来. 从连接池 ...