Since you are a good friend of Jaber and Eyad, they are asking for your help to solve this problem.

You are given a graph consisting of \(n\) nodes, which initially has no edges. For each node \(i\),there's a string \(s_i\)

of lowercase Latin letters written on it.

You have to process \(q\) queries of two types:

  • 1 \(u\) \(v\) : it means add an edge between node uand node v.
  • 2 \(u\) \(t\) : it means for node \(u\) and string \(t\), output the sum of \(cnt_v\) over all nodes \(v\) which belong to the same component as \(u\),where \(cnt_v\) is the number of times \(s_v\) occurs in \(t\) as a substring.

It is guaranteed that the sum of lengths of sv doesn't exceed \(5\times10^5\), and sum of lengths of the query strings doesn't exceed \(5\times10^5\)

1 二进制分组

合并的时候,AC 自动机很难合并,所以考虑定期重构。

对每个点开一个栈,分别表示 \(2^i\) 个串的合并。加入栈时,如果同时存在两个有 \(2^i\) 个串的时候就把他重构成一个 \(2^{i+1}\) 的串。观察到每个串都会被重构 \(\log n\) 次,算上重构,复杂度就 \(O(|S_i|log n|\Sigma|)\)

2.线段树合并。

由于一开始就把所有的串给了出来,所以可以直接给他跑一个 AC 自动机,弄出fail 树。

考虑我后面的询问需要知道什么,需要知道这个点在 fail 树上有多少个祖先是和 \(x\) 在同一个连通块里面的。所以可以用线段树合并去维护这个东西。在第 \(x\) 棵线段树上把 \(dfn_x,dfn_x+sz_x-1\) 这段区间赋值成 \(1\),然后进行线段树合并,单点查询就可以得到答案了。

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5,M=2e5+5;;
int idx,tr[N][26],tme=-1,hd[N],dfn[N],sz[N],u,v,fa[N],op,n,fil[N],q[N],l,r,e_num;
long long ans;
char str[N];
struct edge{
int v,nxt;
}e[N<<1];
string s[M];
void add_edge(int u,int v)
{
e[++e_num]=(edge){v,hd[u]};
hd[u]=e_num;
}
int read()
{
int s=0;
char ch=getchar();
while(ch<'0'||ch>'9')
ch=getchar();
while(ch>='0'&&ch<='9')
s=s*10+ch-48,ch=getchar();
return s;
}
struct segment{
int rt[M],tr[N*30],lc[N*30],rc[N*30],idx;
int merge(int u,int v)
{
if(!u||!v)
return u|v;
tr[u]+=tr[v];
lc[u]=merge(lc[u],lc[v]);
rc[u]=merge(rc[u],rc[v]);
return u;
}
void upd(int&o,int l,int r,int x,int y)
{
if(!o)
o=++idx;
if(x<=l&&r<=y)
{
tr[o]++;
return;
}
int md=l+r>>1;
if(md>=x)
upd(lc[o],l,md,x,y);
if(md<y)
upd(rc[o],md+1,r,x,y);
}
int qry(int&o,int l,int r,int x)
{
if(!o)
return 0;
if(l==r)
return tr[o];
int md=l+r>>1;
if(md>=x)
return qry(lc[o],l,md,x)+tr[o];
return qry(rc[o],md+1,r,x)+tr[o];
}
void mge(int x,int y)
{
rt[y]=merge(rt[y],rt[x]);
}
}b;
void insert(string s,int x)
{
int u=0;
for(int i=0;i<s.size();i++)
{
if(!tr[u][s[i]-'a'])
tr[u][s[i]-'a']=++idx;
u=tr[u][s[i]-'a'];
}
}
void build()
{
l=1,r=0;
for(int i=0;i<26;i++)
if(tr[0][i])
q[++r]=tr[0][i];
while(l<=r)
{
for(int i=0;i<26;i++)
{
if(tr[q[l]][i])
fil[q[++r]=tr[q[l]][i]]=tr[fil[q[l]]][i];
else
tr[q[l]][i]=tr[fil[q[l]]][i];
}
++l;
}
for(int i=1;i<=idx;i++)
add_edge(fil[i],i);
}
void sou(int x)
{
dfn[x]=++tme,sz[x]=1;
for(int i=hd[x];i;i=e[i].nxt)
sou(e[i].v),sz[x]+=sz[e[i].v];
}
int find(int x)
{
if(fa[x]==x)
return x;
return fa[x]=find(fa[x]);
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
scanf("%s",str),insert(s[i]=str,fa[i]=i);
build();
sou(0);
for(int i=1;i<=n;i++)
{
int u=0;
for(int j=0;j<s[i].size();j++)
u=tr[u][s[i][j]-'a'];
b.upd(b.rt[i],0,idx,dfn[u],dfn[u]+sz[u]-1);
}
int q=read();
while(q--)
{
op=read();
if(op==1)
{
u=read(),v=read();
if(find(u)^find(v))
{
b.mge(find(u),find(v));
fa[find(u)]=find(v);
}
}
else
{
ans=0;
u=read(),scanf("%s",str);
u=find(u);
int k=0;
for(int i=0;str[i];i++)
{
k=tr[k][str[i]-'a'];
ans+=b.qry(b.rt[u],0,idx,dfn[k]);
}
printf("%lld\n",ans);
}
}
}
  1. Kruskal重构树。

给询问他建一个 kruskal 重构树,然后一次询问在 kruskal 重构树上是一段连续区间 \([l,r]\) 的询问,可以拆成 \(l-1\) 和 \(r\) 的询问,不断给线段树中加入元素,回答询问即可。

[gym104542F] Interesting String Problem的更多相关文章

  1. FZU - 2218 Simple String Problem(状压dp)

    Simple String Problem Recently, you have found your interest in string theory. Here is an interestin ...

  2. hdu String Problem(最小表示法入门题)

    hdu 3374 String Problem 最小表示法 view code#include <iostream> #include <cstdio> #include &l ...

  3. HDU 3374 String Problem(KMP+最大/最小表示)

    String Problem Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) T ...

  4. 【HDU3374】 String Problem (最小最大表示法+KMP)

    String Problem Description Give you a string with length N, you can generate N strings by left shift ...

  5. HDOJ3374 String Problem 【KMP】+【最小表示法】

    String Problem Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) T ...

  6. HDU 3374 String Problem (KMP+最大最小表示)

    HDU 3374 String Problem (KMP+最大最小表示) String Problem Time Limit: 2000/1000 MS (Java/Others)    Memory ...

  7. String Problem hdu 3374 最小表示法加KMP的next数组

    String Problem Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  8. ACM-ICPC2018南京赛区 Mediocre String Problem

    Mediocre String Problem 题解: 很容易想到将第一个串反过来,然后对于s串的每个位置可以求出t的前缀和它匹配了多少个(EXKMP 或者 二分+hash). 然后剩下的就是要处理以 ...

  9. hdu3374 String Problem【最小表示法】【exKMP】

    String Problem Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  10. hdu 5772 String problem 最大权闭合子图

    String problem 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5772 Description This is a simple pro ...

随机推荐

  1. 用 GPT-4 给开源项目 GoPool 重构测试代码 - 每天5分钟玩转 GPT 编程系列(8)

    目录 1. 好险,差点被喷 2. 重构测试代码 2.1 引入 Ginkgo 测试框架 2.2 尝试改造旧的测试用例 2.3 重构功能测试代码 3. 总结 1. 好险,差点被喷 早几天发了一篇文章:&l ...

  2. Windows 虚拟地址 到底是如何映射到 物理地址 的?

    一:背景 1. 讲故事 我发现有很多的 .NET程序员 写了很多年的代码都没弄清楚什么是 虚拟地址,更不用谈什么是 物理地址 以及Windows是如何实现地址映射的了?这一篇我们就来聊一聊这两者之间的 ...

  3. 记一次weak_up函数绕过

    2023 蓝帽杯CTF LovePHP 因为比赛已经结束,所以复现环境是从本地进行复现,这次比赛本来排名挺靠前的,原本总排名是60多名,赛区排名30多名,本来是以为有希望进入半决赛的,但是没想到比赛结 ...

  4. 我找回了我喜欢的Github Old Feed

    前言 这周Github更新了个人主页Feed(指的是用户的活动源或动态源),作为GitHub重度爱好者而言New Feed完全不是我之前所喜欢的效果.虽然说New Feed添加了允许用户可以自定义配置 ...

  5. Springboot中使用线程池的三种方式

    前言 多线程是每个程序员的噩梦,用得好可以提升效率很爽,用得不好就是埋汰的火葬场. 这里不深入介绍,主要是讲解一些标准用法,熟读唐诗三百首,不会作诗也会吟. 这里就介绍一下springboot中的多线 ...

  6. dedebiz数据重置

    TRUNCATE biz_addonarticle;TRUNCATE biz_addonimages;TRUNCATE biz_addoninfos;TRUNCATE biz_addonshop;TR ...

  7. Solution Set -「ARC 111」

    「ARC 111A」Simple Math 2 Link. \(\lfloor \frac{10^N - kM^2}{M} \rfloor \equiv \lfloor \frac{10^N}{M} ...

  8. 文心一言 VS 讯飞星火 VS chatgpt (105)-- 算法导论10.1 3题

    三.用go语言,仿照图 10-2,画图表示依次执行操作 ENQUEUE(Q,4).ENQUEUE(Q,1).ENQUEUE(Q,3).DEQUEUE(Q).ENQUEUE(Q,8)和 DEQUEUE( ...

  9. C++ bitset 用法和应用

    C++的 bitset 在 bitset 头文件中,它是一种类似数组的结构,它的每一个元素只能是0或1,每个元素仅用1bit空间. 下面是具体用法 构造函数 bitset常用构造函数有四种,如下 bi ...

  10. open3d -- voxel_down_sample

    官网文档 parameter: Input: open3d.geometry.Pointcloud点云类 voxel_size: 体素单位长度 Return: 处理后的点云类 Description: ...