CF587F Duff is Mad(AC自动机+树状数组+分块)
考虑两一个暴力
1
因为询问\([a,b]\)可以拆成\([1,b]\)-\([1,a-1]\)所以把询问离线,然后就是求\([1,x]\)中被\(S_i\)包含的串的数量。考虑当\([1,x-1]->[1,x]\)时我们把\(S_x\)结束节点在fail树的子树加1。然后询问就是求\(S_i\)在AC自动机上跑时经过所有点的点权用树状数组维护。设\(\sum{len[S_i]}=L\)这样的复杂度就是\(O(mLlogL)\)无法通过此题。
2
依然离线。这次我们把\(S_i\)放在fail树上跑时经过的点在fail树上加1。然后每一个x的贡献就是x的子树和。这个我们在fail树上DP(合并size)再做一个前缀合就能做到\(O(n)\)预处理\(O(1)\)查询。复杂度\(O(nL)\)无法通过此题。
然后我们对询问分块。长度大于\(sqrt(L)\)的用方法二,其他的用方法一。这样方法二最多用\(\sqrt{n}\)次。复杂度\(O(\sqrt{n}L)\)。方法一因为么次询问的串长最多为\(\sqrt{L}\),所以复杂度为\(O(m\sqrt{L}logL)\)最终复杂度就是这两个加起来。可以通过此题。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int N=201000;
const int M=501000;
int cnt,head[N];
int n,m;
long long ans[M];
int dfn[N],R[N],L[N],tot,LEN;
long long tr[N];
long long size[N],sum[N];
string s[N];
struct ques{
int x,k,id;
ques(int xx=0,int kk=0,int idd=0){
x=xx;k=kk;id=idd;
}
};
vector<ques> vec1[N],vec2[N];
struct edge{
int to,nxt;
}e[N];
void add_edge(int u,int v){
cnt++;
e[cnt].nxt=head[u];
e[cnt].to=v;
head[u]=cnt;
}
int lowbit(int x){
return x&-x;
}
void add(int x,int w){
for(int i=x;i<=tot;i+=lowbit(i))tr[i]+=w;
}
long long getsum(int x){
long long tmp=0;
for(int i=x;i;i-=lowbit(i))tmp+=tr[i];
return tmp;
}
void dfs(int u){
dfn[u]=++tot;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
dfs(v);
}
R[u]=tot;
}
void dfs1(int u){
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
dfs1(v);
size[u]+=size[v];
}
}
struct AC{
int point[N],trans[N][27],tot,fail[N];
void ins(string s,int len,int k){
int now=0;
for(int i=0;i<len;i++){
if(trans[now][s[i]-'a'+1]==0)trans[now][s[i]-'a'+1]=++tot;
now=trans[now][s[i]-'a'+1];
}
point[k]=now;
}
void get_fail(){
queue<int> q;
for(int i=1;i<=26;i++)if(trans[0][i])q.push(trans[0][i]);
while(!q.empty()){
int now=q.front();
q.pop();
for(int i=1;i<=26;i++){
if(trans[now][i])fail[trans[now][i]]=trans[fail[now]][i],q.push(trans[now][i]);
else trans[now][i]=trans[fail[now]][i];
}
}
}
}ac;
int read(){
int sum=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return sum*f;
}
int main(){
n=read();m=read();
for(int i=1;i<=n;i++){
cin>>s[i];
L[i]=s[i].size();
LEN+=L[i];
ac.ins(s[i],L[i],i);
}
int hhh=getchar();
int block=sqrt(LEN);
ac.get_fail();
for(int i=1;i<=ac.tot;i++)add_edge(ac.fail[i],i);
dfs(0);
for(int i=1;i<=m;i++){
int a=read(),b=read(),c=read();
if(L[c]>block){
vec1[c].push_back(ques(b,1,i)),vec1[c].push_back(ques(a-1,-1,i));
}
else vec2[a-1].push_back(ques(c,-1,i)),vec2[b].push_back(ques(c,1,i));
}
for(int i=1;i<=n;i++){
int now=0;
for(int j=0;j<L[i];j++){
now=ac.trans[now][s[i][j]-'a'+1];
if(j==L[i]-1)add(dfn[now],1),add(R[now]+1,-1);
}
for(int j=0;j<vec2[i].size();j++){
int now=0;
for(int k=0;k<L[vec2[i][j].x];k++){
now=ac.trans[now][s[vec2[i][j].x][k]-'a'+1];
ans[vec2[i][j].id]+=(long long)vec2[i][j].k*getsum(dfn[now]);
}
}
if(vec1[i].size()){
for(int j=0;j<=tot;j++)size[j]=sum[j]=0;
int now=0;
for(int j=0;j<L[i];j++)now=ac.trans[now][s[i][j]-'a'+1],size[now]++;
dfs1(0);
for(int j=1;j<=n;j++)sum[j]=sum[j-1]+size[ac.point[j]];
for(int j=0;j<vec1[i].size();j++)
ans[vec1[i][j].id]+=(long long)vec1[i][j].k*sum[vec1[i][j].x];
}
}
for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
return 0;
}
CF587F Duff is Mad(AC自动机+树状数组+分块)的更多相关文章
- 洛谷P2414 阿狸的打字机 [NOI2011] AC自动机+树状数组/线段树
正解:AC自动机+树状数组/线段树 解题报告: 传送门! 这道题,首先想到暴力思路还是不难的,首先看到y有那么多个,菜鸡如我还不怎么会可持久化之类的,那就直接排个序什么的然后按顺序做就好,这样听说有7 ...
- 【BZOJ】2434: [Noi2011]阿狸的打字机 AC自动机+树状数组+DFS序
[题意]阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小写 ...
- Codeforces 587F - Duff is Mad(根号分治+AC 自动机+树状数组)
题面传送门 第一眼看成了 CF547E-- 话说 CF587F 和 CF547E 出题人一样欸--还有另一道 AC 自动机的题 CF696D 也是这位名叫 PrinceOfPersia 的出题人出的- ...
- BZOJ2434: [Noi2011]阿狸的打字机(AC自动机 树状数组)
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 4140 Solved: 2276[Submit][Status][Discuss] Descript ...
- BZOJ3881[Coci2015]Divljak——AC自动机+树状数组+LCA+dfs序+树链的并
题目描述 Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. ...
- HDU 6096 String(AC自动机+树状数组)
题意 给定 \(n\) 个单词,\(q\) 个询问,每个询问包含两个串 \(s_1,s_2\),询问有多少个单词以 \(s_1\) 为前缀, \(s_2\) 为后缀,前后缀不能重叠. \(1 \leq ...
- bzoj 2434 AC自动机+树状数组
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 3493 Solved: 1909[Submit][Sta ...
- [NOI2011]阿狸的打字机 --- AC自动机 + 树状数组
[NOI2011] 阿狸的打字机 题目描述: 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. 打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现, ...
- 【dfs序+AC自动机+树状数组】BZOJ2434-[Noi2011]阿狸的打字机
[题目大意] 输入一个字符串,其中:(1)a..z:在字符串末尾添加当前字符(2)P:输出当前字符串(3)B:从当前字符串末尾删去一个字符. 给出m组查询,输出第i个输出的字符串在第j个输出的字符串内 ...
随机推荐
- 02 C#高级
第九天 面向过程--à面向对象 面向过程:面向的是完成这件事儿的过程,强调的是完成这件事儿的动作. 把大象塞进冰箱 1. 打开冰箱门 2. 把大象塞进去,亲下大象的屁股 3. 关闭冰箱门 孙全 瘦小 ...
- [CTSC1999][网络流24题]家园
题目:洛谷P2754. 题目大意:有$n$个空间站,$m$个飞船,每个飞船有各自的停靠站点,并且从第一个停靠站点开始,不断循环.每个飞船有不同的容量(-1为月球,0为地球).每个飞船初始停在第一个停靠 ...
- -java转json hibernate懒加载造成的无限递归问题
1.在判断到底是谁维护关联关系时,可以通过查看外键,哪个实体类定义了外键,哪个类就负责维护关联关系. JoinColumn(name="pid") 2. 在保存数据时,总是先保存的 ...
- C++函数传递数组的两种方式
数组与指针. 传首地址过去,然后通过地址输出数组元素. 1.一维数组 #include<iostream> using namespace std; #include <cstrin ...
- vue父组件引用子组件方法显示undefined问题原因及解决方法
关于vue父组件引用子组件问题 1.首先导入子组件并且在components中定义子组件 2.引用子组件,并定义ref,ref定义的名称用于 this.$refs所调用的名称 3.调用子组件的方法 ( ...
- Unity 多场景打包
本文章由cartzhang编写,转载请注明出处. 所有权利保留. 文章链接:http://blog.csdn.net/cartzhang/article/details/50580641 作者:car ...
- Vue-router入门
1.npm install vue-router --save-dev 安装路由包,在安装脚手架时实际上可以直接安装 2.解读核心文件 router/index.js文件 import Vue fro ...
- Ubuntu系统的Redis安装配置
Ubuntu系统的Redis安装配置 一. 安装Redis: 在Ubuntu系统下安装Redis数据库有两种方式: 方式一:下载最新的Redis版本(tar.gz格式),解压安装.操作如下: ...
- JAVA学习之 异常处理机制
今天就来说说java的异常处理机制,异常处理不是第一接触,尤其是写过非常多c#的代码,基本都会写到异常处理的代码,事实上c#的异常处理与java的异常处理基本都是一样的,仅仅是在一些细节上不是非常一样 ...
- Cocos2d-x使用Luajit将Lua脚本编译为bytecode,实现加密 更新
项目要求对lua脚本进行加密,查了一下相关的资料 ,得知lua本身能够使用luac将脚本编译为字节码(bytecode)从而实现加密,试了一下,确实可行. 以下是使用原生的lua解释器编译字节码: 1 ...