题目

有趣的思想

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

我们发现无论怎么优化好像都不是很可行,考虑一下对根号优化

对于长度大于\(\sqrt{n}\)的串,显然这样的串也不会超过\(\sqrt{n}\)个,我们把这些串在\(AC\)机上跑一遍,之后统计一下子树和,统计一个前缀和就可以回答询问了

这样复杂度是\(O(n\sqrt{n})\)

对于长度小于\(\sqrt{n}\)的串,我们允许把每一个串都放到\(AC\)自动机上跑一遍,于是可以直接用主席树来查一下跑出来的节点到根有多少个标记在\(l\)到\(r\)之间

这边的复杂度是\(O(n\sqrt{n}logn)\)

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<queue>
#define maxn 100005
#define M 4000005
#define re register
#define LL long long
#define int long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
int x=0;char c=getchar();while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
struct E{int v,nxt;}e[maxn];
struct Ask{int l,r,k,rk,o;}q[maxn];
int n,m,cnt,tot,num,sz;
int fail[maxn],son[maxn][26],len[maxn],head[maxn],a[maxn];
LL sum[maxn],pre[maxn],Ans[maxn];
int rt[maxn],ls[M],rs[M],d[M];
std::string S[maxn];
char T[maxn];
std::vector<int> v[maxn];
inline int cmp1(Ask A,Ask B) {if(A.o==B.o)return A.k<B.k;return A.o>B.o;}
inline void add(int x,int y) {e[++num].v=y;e[num].nxt=head[x];head[x]=num;}
inline void work() {for(re int i=1;i<=n;i++) pre[i]=pre[i-1]+sum[a[i]];}
inline void ins(int i) {
scanf("%s",T);
S[i]=T;len[i]=S[i].size();tot+=len[i];
int now=0;
for(re int j=0;j<S[i].size();j++) {
if(!son[now][S[i][j]-'a']) son[now][S[i][j]-'a']=++cnt;
now=son[now][S[i][j]-'a'];
}
v[now].push_back(i);a[i]=now;
}
inline void Build() {
std::queue<int> q;
for(re int i=0;i<26;i++) if(son[0][i]) q.push(son[0][i]);
while(!q.empty()) {
int k=q.front();q.pop();
for(re int i=0;i<26;i++)
if(son[k][i]) fail[son[k][i]]=son[fail[k]][i],q.push(son[k][i]);
else son[k][i]=son[fail[k]][i];
}
}
inline int change(int pre,int x,int y,int pos) {
int root=++sz;
d[root]=d[pre]+1;
if(x==y) return root;
ls[root]=ls[pre],rs[root]=rs[pre];
int mid=x+y>>1;
if(pos<=mid) ls[root]=change(ls[pre],x,mid,pos);
else rs[root]=change(rs[pre],mid+1,y,pos);
return root;
}
inline int query(int p,int x,int y,int pos) {
if(!pos) return 0;
if(x==y) return d[p];
int mid=x+y>>1;
if(pos<=mid) return query(ls[p],x,mid,pos);
return d[ls[p]]+query(rs[p],mid+1,y,pos);
}
inline void Query(int i) {
int now=0;
for(re int j=0;j<len[i];j++)
now=son[now][S[i][j]-'a'],sum[now]++;
}
inline void dfs(int x) {
for(re int i=head[x];i;i=e[i].nxt) {
dfs(e[i].v),sum[x]+=sum[e[i].v];
}
}
inline void Dfs(int x) {
for(re int i=head[x];i;i=e[i].nxt) {
rt[e[i].v]=rt[x];
for(re int j=0;j<v[e[i].v].size();j++) rt[e[i].v]=change(rt[e[i].v],1,n,v[e[i].v][j]);
Dfs(e[i].v);
}
}
signed main() {
n=read(),m=read();
for(re int i=1;i<=n;i++) ins(i);
tot=std::sqrt(tot);Build();
for(re int i=1;i<=m;i++)
q[i].l=read(),q[i].r=read(),q[i].k=read(),q[i].rk=i,q[i].o=(len[q[i].k]>tot);
std::sort(q+1,q+m+1,cmp1);
for(re int i=1;i<=cnt;i++) add(fail[i],i);
Dfs(0);
int L=1,R=1;Query(q[1].k);dfs(0),work();
for(re int i=2;i<=m+1;i++) {
if(len[q[i].k]<=tot) {for(re int j=L;j<i;j++) Ans[q[j].rk]=pre[q[j].r]-pre[q[j].l-1];R=i;break;}
if(q[i].k!=q[i-1].k) {
for(re int j=L;j<i;j++) Ans[q[j].rk]=pre[q[j].r]-pre[q[j].l-1];
memset(sum,0,sizeof(sum));
Query(q[i].k);dfs(0);work();L=i;
}
}
for(re int i=R;i<=m;i++) {
int now=0;
for(re int j=0;j<len[q[i].k];j++)
now=son[now][S[q[i].k][j]-'a'],Ans[q[i].rk]+=query(rt[now],1,n,q[i].r)-query(rt[now],1,n,q[i].l-1);
}
for(re int i=1;i<=m;i++) printf("%I64d\n",Ans[i]);
return 0;
}

CF587F Duff is Mad的更多相关文章

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

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

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

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

  3. 【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|, ...

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

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

  5. CF数据结构练习

    1. CF 438D The Child and Sequence 大意: n元素序列, m个操作: 1,询问区间和. 2,区间对m取模. 3,单点修改 维护最大值, 取模时暴力对所有>m的数取 ...

  6. cf Round 587

    A.Duff and Weight Lifting(思维) 显然题目中只有一种情况可以合并 2^a+2^a=2^(a+1).我们把给出的mi排序一下,模拟合并操作即可. # include <c ...

  7. Lesson 21 Mad or not?

    Text Aeroplanes are slowly driving me mad. I live near an airport and passing planes can be heard ni ...

  8. Codeforces Round #326 (Div. 2) B. Pasha and Phone C. Duff and Weight Lifting

    B. Pasha and PhonePasha has recently bought a new phone jPager and started adding his friends' phone ...

  9. 【转】Duff's Device

    在看strcpy.memcpy等的实现发现用了内存对齐,每一个word拷贝一次的办法大大提高了实现效率,参加该blog(http://totoxian.iteye.com/blog/1220273). ...

随机推荐

  1. wcf 登录认证 angular 认证重定向

    自定义认证管理器,分为两级:1.登陆认证.2.权限认证.权限主要是用户.角色.角色用户关系.功能(系统资源).角色功能关系,5部分决定用户的权限(视图). 两层认证都通过后,更新session的最新交 ...

  2. java 并发(五)---AbstractQueuedSynchronizer(2)

           文章部分代码和照片来自参考资料 问题 : ConditionObject  的 await 和 signal 方法是如何实现的 ConditonObject ConditionObjec ...

  3. [android] 练习viewpagerindicator的使用(一)

    主要是学习一下使用这个库 activity_main.xml <?xml version="1.0" encoding="utf-8"?> < ...

  4. Excel的Sheet页复制

    最近在做一个项目,其中涉及基于模板对Excel的Sheet页进行复制.在网上尝试了很多,发现都不够完美,苦恼. 然后在查阅资料的过程中,发现有一篇提及,POI的API只对同一个Excel文件中的She ...

  5. Effective C++ .10,11 operator=的约定与注意

    1. 返回一个reference to *this 返回一个指向自身的引用符合惯例,可以进行如(a=c).modify()类似的操作,即可以形成链式操作,否则修改的只是一个临时对象.这个和Java中常 ...

  6. COGS2216 你猜是不是KMP

    第一道自己写的FFT...... 不知为啥这题在网上找不到题解......真是麻烦,害得我推了半天...... 还是写个简要题解吧...... 首先把S和T拆成序列,a~z分别对应成1~26,?是0, ...

  7. CRLF与LF解析

    window和mac的同学合作开发项目,会出现git提交/拉取时换行符不一致导致,提示 "the text is identical, but the files do not match, ...

  8. [小北De编程手记] : Lesson 03 - Selenium For C# 之 元素定位

    无论哪一种自动化测试的驱动框架(基于B/S,桌面应用,还是手机App).都应当具有一套优秀的元素定位技术.通常的自动化测试流程也可以简单的归结为是一个从被测试程序中识别或是定位元素以及执行操作和验证元 ...

  9. 【免费公测】阿里云SSD云盘,不仅仅是IO提速10倍

    今天很高兴为大家介绍最新的ECS存储服务:SSD云盘. SSD云盘基于全SSD存储介质.利用阿里云飞天分布式存储技术,提供数据可靠性99.999%的高性能存储:该产品具备以下特点: l  高性能:单个 ...

  10. 重建二叉树(C++和Python实现)

    (说明:本博客中的题目.题目详细说明及参考代码均摘自 “何海涛<剑指Offer:名企面试官精讲典型编程题>2012年”) 题目 输入某二叉树前序遍历和中序遍历结果,请重建出该二叉树.假设输 ...