【CF587F】Duff is Mad

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

$\sum|s_i|,q\le 10^5$

题解:先将询问离线,改成前缀相减。然后建出AC自动机,考虑分块。

对于长度$>\sqrt n$的询问串,这种串最多$\sqrt n$个,我们每次可以扫一遍整个fail树,处理出每个节点到根的路径上有多少个询问串中的点。然后将所有串一个一个加入到fail树里,假如加入的串的结束节点到根路径上有a个询问串种的点,则答案+=a。

对于长度$<\sqrt n$的串,我们按编号一个一个处理。我们加入一个串时,要将其结束节点的fail树子树中所有节点的点权都+1。放到DFS序上就是区间+操作。我们再次分块便可做到$O(1)-O(\sqrt n)$的复杂度。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int maxn=100010;
typedef long long ll;
int n,m,B,tot,cnt;
ll sum;
struct node
{
int ch[26],fail;
}p[maxn];
struct query
{
int x,k,org;
query() {}
query(int a,int b,int c) {x=a,k=b,org=c;}
};
int lp[maxn],rp[maxn],q[maxn],pos[maxn],head[maxn],to[maxn],nxt[maxn],p1[maxn],p2[maxn];
ll ans[maxn],s[maxn],sb[1000];
char str[maxn];
vector<query> v1[maxn],v2[maxn];
vector<query>::iterator it;
inline int rd()
{
int ret=0,f=1; char gc=getchar();
while(gc<'0'||gc>'9') {if(gc=='-') f=-f; gc=getchar();}
while(gc>='0'&&gc<='9') ret=ret*10+(gc^'0'),gc=getchar();
return ret*f;
}
inline void build()
{
int i,u,h=1,t=0;
q[++t]=1;
while(h<=t)
{
u=q[h++];
for(i=0;i<26;i++)
{
if(p[u].ch[i])
{
q[++t]=p[u].ch[i];
if(u==1) p[p[u].ch[i]].fail=1;
else p[p[u].ch[i]].fail=p[p[u].fail].ch[i];
}
else
{
if(u==1) p[u].ch[i]=1;
else p[u].ch[i]=p[p[u].fail].ch[i];
}
}
}
}
bool cmp(const query &a,const query &b)
{
return a.x<b.x;
}
inline void add(int a,int b)
{
to[cnt]=b,nxt[cnt]=head[a],head[a]=cnt++;
}
void dfs(int x)
{
p1[x]=++p2[0];
for(int i=head[x];i!=-1;i=nxt[i]) dfs(to[i]);
p2[x]=p2[0];
}
inline void upd(int a,int b)
{
int i,c=a/B,d=b/B;
if(c==d) for(i=a;i<=b;i++) s[i]++;
else
{
for(i=a;i<c*B+B;i++) s[i]++;
for(i=d*B;i<=b;i++) s[i]++;
for(i=c+1;i<d;i++) sb[i]++;
}
}
int main()
{
n=rd(),m=rd();
int i,j,u,a,b,c;
tot=1;
for(i=1;i<=n;i++)
{
lp[i]=rp[i-1],scanf("%s",str+lp[i]),rp[i]=lp[i]+strlen(str+lp[i]);
for(u=1,j=lp[i];j<rp[i];j++)
{
a=str[j]-'a';
if(!p[u].ch[a]) p[u].ch[a]=++tot;
u=p[u].ch[a];
}
pos[i]=u;
}
build(),B=int(sqrt(double(tot+1)));
for(i=1;i<=m;i++)
{
a=rd(),b=rd(),c=rd();
if(rp[c]-lp[c]>B)
{
if(a!=1) v2[c].push_back(query(a-1,-1,i));
v2[c].push_back(query(b,1,i));
}
else
{
if(a!=1) v1[a-1].push_back(query(c,-1,i));
v1[b].push_back(query(c,1,i));
}
}
memset(head,-1,sizeof(head));
for(i=2;i<=tot;i++) add(p[i].fail,i);
dfs(1);
for(i=1;i<=n;i++)
{
upd(p1[pos[i]],p2[pos[i]]);
for(it=v1[i].begin();it!=v1[i].end();it++)
{
a=(*it).x,b=(*it).k,c=(*it).org;
for(u=1,j=lp[a];j<rp[a];j++)
{
u=p[u].ch[str[j]-'a'];
ans[c]+=(s[p1[u]]+sb[p1[u]/B])*b;
}
}
}
for(i=1;i<=n;i++) if(rp[i]-lp[i]>B&&v2[i].size())
{
memset(s,0,sizeof(s));
for(u=1,j=lp[i];j<rp[i];j++) u=p[u].ch[str[j]-'a'],s[u]++;
for(j=tot;j>=2;j--) s[p[q[j]].fail]+=s[q[j]];
sort(v2[i].begin(),v2[i].end(),cmp);
for(sum=0,j=1,it=v2[i].begin();it!=v2[i].end();it++)
{
a=(*it).x,b=(*it).k,c=(*it).org;
while(j<=a) sum+=s[pos[j++]];
ans[c]+=b*sum;
}
}
for(i=1;i<=m;i++) printf("%lld\n",ans[i]);
return 0;
}//

【CF587F】Duff is Mad AC自动机+分块的更多相关文章

  1. [CF587F]Duff is Mad[AC自动机+根号分治+分块]

    题意 给你 \(n\) 个串 \(s_{1\cdots n}\) ,每次询问给出 \(l,r,k\) ,问在 \(s_{l\cdots r}\) 中出现了多少次 \(s_k\) . \(n,q,\su ...

  2. HDU4787 GRE Words Revenge【AC自动机 分块】

    HDU4787 GRE Words Revenge 题意: \(N\)次操作,每次记录一个\(01\)串或者查询一个\(01\)串能匹配多少个记录的串,强制在线 题解: 在线的AC自动机,利用分块来降 ...

  3. HDU4787 GRE Words Revenge(AC自动机 分块 合并)

    题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=4787 Description Now Coach Pang is preparing for ...

  4. CF587F Duff is Mad(AC自动机+树状数组+分块)

    考虑两一个暴力 1 因为询问\([a,b]\)可以拆成\([1,b]\)-\([1,a-1]\)所以把询问离线,然后就是求\([1,x]\)中被\(S_i\)包含的串的数量.考虑当\([1,x-1]- ...

  5. CF587F Duff is Mad

    题目 有趣的思想 首先暴力的话,自然是对每一个询问在\(AC\)自动机上跑一遍\(k\),看看跑出来的节点在\(fail\)树到根的路径上有多少个\(l\)到\(r\)之间的结束标记就好了 我们发现无 ...

  6. Codeforces 587F - Duff is Mad(根号分治+AC 自动机+树状数组)

    题面传送门 第一眼看成了 CF547E-- 话说 CF587F 和 CF547E 出题人一样欸--还有另一道 AC 自动机的题 CF696D 也是这位名叫 PrinceOfPersia 的出题人出的- ...

  7. CF587F-Duff is Mad【AC自动机,根号分治】

    正题 题目链接:https://www.luogu.com.cn/problem/CF587F 题目大意 给出\(n\)个字符串\(s\).\(q\)次询问给出\(l,r,k\)要求输出\(s_{l. ...

  8. hdu4787 AC自动机加分块

    这题说的是 有n次操作 +w 表示读入一个字符串,?p 询问这个字符串的子串在那些模板串中有多少个, http://blog.csdn.net/qq574857122/article/details/ ...

  9. 一道叉姐的AC自动机鬼题

    题面描述丢失了... 给n个串模板串,然后再给你m个串,对于这m个串的每个串,问在[L,R]的模板串中,在多少个串中出现过; 这题的正解是对于后m个串建AC自动机,然后离线,在fail树上树链求并. ...

随机推荐

  1. 深入理解Git (一) - 元数据

    三分钟教你学Git仅仅是教我们会用Git了,但是内部的一些实现原理假设我们也知道一些的话会使我们使用起来更加顺手.这个系列就是在你用了Git一段时间之后,并想继续到Git的内部看一下到底而准备的. G ...

  2. Json解析包FastJson使用

    阿里巴巴FastJson是一个Json处理工具包,包括“序列化”和“反序列化”两部分,它具备如下特征:速度最快,测试表明,fastjson具有极快的性能,超越任其他的Java Json parser. ...

  3. phpbbchina恢复上线

    上个月已经把ICP备案重新办过了, 但是一直在忙着应付工作上的事. 从上周末开始经过数天的努力, 将 phpbbchina 恢复上线了. 时间一晃, 正好十年. 目前能找到的最新的数据是2008-10 ...

  4. Android 手动调用 返回键

    有人想通过下面代码来实现手动调用返回键,很可惜会出现空指针异常. this.onKeyDown(KeyEvent.KEYCODE_BACK, null); 我们可以通过调用 onBackPressed ...

  5. 5.翻译系列:EF 6中数据库的初始化(EF 6 Code-First 系列)

    原文地址:http://www.entityframeworktutorial.net/code-first/database-initialization-in-code-first.aspx EF ...

  6. Android 组件系列-----Activity初步

    在上篇博文讲解了Android的Activity这个组件的启动流程后,接下来我们就来看看我们的Activity与我们的布局文件的关系吧 我们先来看看一个最简单的布局文件的内容: <Relativ ...

  7. 如何在servlet刚启动时候获取服务器根目录?

    public class InitServlet extends HttpServlet{ public static String root; @Override public void init( ...

  8. 菜鸟教程之工具使用(七)——从GIt上导出Maven项目

    今天继续我们的工具教程,公司用Git作为版本控制工具,所以最近一直在跟Git打交道.也是一边学习一边使用,于是想做一些入门教程,一来自己总结一下,二来还能帮助一些刚刚接触Git的朋友.一举两得,何乐而 ...

  9. 【Linux高级驱动】linux设备驱动模型之平台设备驱动机制

    [1:引言: linux字符设备驱动的基本编程流程] 1.实现模块加载函数  a.申请主设备号    register_chrdev(major,name,file_operations);  b.创 ...

  10. Asp.Net T4模板生成三层架构

    1.T4 Editor安装 T4:根据模板生成文件,例如model等 vs中默认t4模板编码是没有提示和高亮的,需使用以下插件,免费的 https://t4-editor.tangible-engin ...