【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|,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自动机+分块的更多相关文章
- [CF587F]Duff is Mad[AC自动机+根号分治+分块]
题意 给你 \(n\) 个串 \(s_{1\cdots n}\) ,每次询问给出 \(l,r,k\) ,问在 \(s_{l\cdots r}\) 中出现了多少次 \(s_k\) . \(n,q,\su ...
- HDU4787 GRE Words Revenge【AC自动机 分块】
HDU4787 GRE Words Revenge 题意: \(N\)次操作,每次记录一个\(01\)串或者查询一个\(01\)串能匹配多少个记录的串,强制在线 题解: 在线的AC自动机,利用分块来降 ...
- HDU4787 GRE Words Revenge(AC自动机 分块 合并)
题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=4787 Description Now Coach Pang is preparing for ...
- CF587F Duff is Mad(AC自动机+树状数组+分块)
考虑两一个暴力 1 因为询问\([a,b]\)可以拆成\([1,b]\)-\([1,a-1]\)所以把询问离线,然后就是求\([1,x]\)中被\(S_i\)包含的串的数量.考虑当\([1,x-1]- ...
- CF587F Duff is Mad
题目 有趣的思想 首先暴力的话,自然是对每一个询问在\(AC\)自动机上跑一遍\(k\),看看跑出来的节点在\(fail\)树到根的路径上有多少个\(l\)到\(r\)之间的结束标记就好了 我们发现无 ...
- Codeforces 587F - Duff is Mad(根号分治+AC 自动机+树状数组)
题面传送门 第一眼看成了 CF547E-- 话说 CF587F 和 CF547E 出题人一样欸--还有另一道 AC 自动机的题 CF696D 也是这位名叫 PrinceOfPersia 的出题人出的- ...
- CF587F-Duff is Mad【AC自动机,根号分治】
正题 题目链接:https://www.luogu.com.cn/problem/CF587F 题目大意 给出\(n\)个字符串\(s\).\(q\)次询问给出\(l,r,k\)要求输出\(s_{l. ...
- hdu4787 AC自动机加分块
这题说的是 有n次操作 +w 表示读入一个字符串,?p 询问这个字符串的子串在那些模板串中有多少个, http://blog.csdn.net/qq574857122/article/details/ ...
- 一道叉姐的AC自动机鬼题
题面描述丢失了... 给n个串模板串,然后再给你m个串,对于这m个串的每个串,问在[L,R]的模板串中,在多少个串中出现过; 这题的正解是对于后m个串建AC自动机,然后离线,在fail树上树链求并. ...
随机推荐
- oracle的start with connect by prior如何使用
oracle的start with connect by prior是根据条件递归查询"树",分为四种使用情况: 第一种:start with 子节点ID='...' connec ...
- verilog语法实例学习(12)
verilog中的综合和不可综合总结 Verilog中综合的概念 综合就是EDA工具或者说综合工具把我们编写的verilog代码转化成具体电路的过程.Verilog中有很多语法,结构,过程,语句,有些 ...
- C# ConcurrentQueue实现
我们从C# Queue 和Stack的实现知道Queue是用数组来实现的,数组的元素不断的通过Array.Copy从一个数组移动到另一个数组,ConcurrentQueue我们需要关心2点:1线程安全 ...
- GraphQL入门1
1. 资源: 主站: https://graphql.org/ 中文站: http://graphql.cn 入门视频: https://graphql.org/blog/rest-api-graph ...
- js获取过滤条件中参数的快捷方式
// window.location.href = "topupRecordController.do?exportExcel&" + encodeURI($(" ...
- 《Essential C++》读书笔记 之 C++编程基础
<Essential C++>读书笔记 之 C++编程基础 2014-07-03 1.1 如何撰写C++程序 头文件 命名空间 1.2 对象的定义与初始化 1.3 撰写表达式 运算符的优先 ...
- VS2013 未找到与约束ContractName Microsoft.VisualStudio.Text.ITextDocumentFactoryService
全是2012版本的 没找到2013的! 控制面板>程序>程序和功能 找到如下选中软件右击修复 即可 需关闭VS2013 参考:https://blog.csdn.net/zhaoyun9 ...
- C#中怎么判断一个数组中是否存在某个数组值
(1) 第一种方法: ,,}; ); // 这里的1就是你要查找的值 ) // 不存在 else // 存在 (2) 第二种方法: string[] strArr = {"a",& ...
- 【iCore4 双核心板_ARM】例程二十二:LWIP_UDP实验——以太网数据传输
实验现象: 核心代码: int main(void) { system_clock.initialize(); led.initialize(); adc.initialize(); delay.in ...
- 【iCore1S 双核心板_ARM】例程九:DAC实验——输出直流电压
实验原理: STM32内部集成12位DAC,可以配置成12位或8位,DAC具有两个独立转换通道, 在双DAC模式下,DA转换可被配置成独立模式或工作模式,iCore1S中DAC参考电压为 2.5V.本 ...