P10176 「OICon-02」Native Faith 题解

news/2025/11/20 19:26:16/文章来源:https://www.cnblogs.com/memset234/p/19247793

Sol

由于 \(f(a,b,c)=\sum\limits_{i=1}^{|a|}\sum\limits_{j=i}^{|a|}\sum\limits_{k=1}^{|b|}\sum\limits_{l=k}^{|b|}[a_{i,i+1,\cdots,j}+b_{k,k+1,\cdots,l} = c]\),所以 \(a\) 一定是 \(c\) 的前缀,\(b\) 一定是 \(c\) 的后缀,所以我们对 \(s_k\) 每个前缀和后缀考虑。

如果我们设 \(f_{i}\) 表示 \(s_k\)\(i\) 个字符在 \(s_{l \sim r}\) 的出现次数,\(g_{i}\) 表示 \(s_k\) 从第 \(i\) 个字符开始一直到末尾在 \(s_{l \sim r}\) 的出现次数,那么答案显然是 \(\sum f_i g_{i+1}\) 的。

那我们考虑把 \(f_{i}\)\(g_{i}\) 求出来,单独求这个东西肯定是可以拆一下,变成在 \(s_{1 \sim r}\) 的出现次数减去在 \(s_{1 \sim l-1}\) 的出现次数的。

以下我们只考虑求 \(f_{i}\),因为求 \(g_{i}\) 可以把 \(s_{i}\) 全部取反,是类似的。

你关注一下这个形式,求 \(s_{1 \sim x}\)\(s_k\) 的出现次数,这个东西不是很版吗?先建出 AC 自动机,然后离线下来扫,每次插入一个 \(s_{i}\),就是在 AC 自动机上把根节点到 \(s_i\) 这条路径点权都加 \(1\),查询就是在 fail 树上节点 \(s_k\) 的子树和。

你发现查询的次数特别多,有 \(\sum |s_k|\) 次,然而修改只有 \(\sum |s_i| = m\) 次,所以用分块平衡一下复杂度,让查询复杂度 \(O(1)\),修改复杂度 \(O(\sqrt{m})\) 即可做到 \(O(\sum |s_k| + m \sqrt m)\) 的复杂度。

然而就算这样,\(O(\sum |s_k|) = O(qm)\) 的复杂度仍然接受不了。

我们考虑根号分治,设阀值为 \(B\)

如果 \(|s_k| \le 2B\),是可以按上面部分来做的,时间复杂度 \(O(q B+m \sqrt m)\),空间复杂度 \(O(q B)\)

否则 \(|s_k| > 2B\),我们对于 \(f_i\),我们分成 \(i \le B\)\(B < i < |s_k|-B\)\(|s_k|-B \le i\) 三个部分分别求。

对于 \(i \le B\)\(|s_k|-B \le i\)\(f_i\) 我们依然可以像上面那样求,时间复杂度 \(O(q B)\)

对于其他的部分一定满足 \(i>B\),此时这个前缀只可能由 \(|s_i| > B\) 的字符串 \(s_i\) 作贡献,而这样的字符串 \(s_i\) 只有 \(O(\frac{m}{B})\) 个,而且 \(i < |s_k|-B\),所以后缀也是只有 \(O(\frac{m}{B})\) 个的,有因为 \(|s_k| > 2B\),所以 \(k\) 的个数也只有 \(O(\frac{m}{B})\) 个。

\(|s_i| > B\),我们设 \(v_i = 1\),否则 \(v_i = 0\)

因此假设我们处理第 \(l\)\(v_i = 1\) 的串到第 \(r\)\(v_i = 1\) 的串对 \(k\)\(f\) 的贡献时,\((k,l,r)\) 的个数是 \(O(\frac{m^3}{B^3})\) 级别的。

这个东西我们继续拆成前 \(r\)\(v_i = 1\) 的串的贡献减去前 \(l-1\)\(v_i = 1\) 的串的贡献。

我们设 \(sum_{i,k,j}\) 表示前 \(i\)\(v=1\) 的串,对 \(k\)\(f_j\) 造成了多少贡献,这里使用了 \(O(\frac{m^2}{B})\) 的空间,预处理的时间复杂度也是 \(O(\frac{m^2}{B})\)

求答案时我们求出 \([l,r]\) 对应的 \(v=1\)\([l',r']\),每个 \((k,l',r')\) 我们都只进行一次暴力求解,对于每个不同的位置 \(k\),此时 \(\sum|s_k| = m\),因此对于不同的 \(k\) 只会暴力遍历 \(m\) 遍,又因为不同的 \(l'\)\(r'\) 对相同的 \(k\) 会多次枚举,最多枚举 \(O(\frac{m^2}{B^2})\) 次,故时间复杂度是 \(O(\frac{m^3}{B^2})\) 的,空间复杂度是 \(O(\frac{m^3}{B^3})\) 的。

时间复杂度 \(O(q B+ m \sqrt m + \frac{m^2}{B} + \frac{m^3}{B^2})\),空间复杂度 \(O(qB + \frac{m^2}{B} + \frac{m^3}{B^3})\)

\(n,m,q\) 同阶,取 \(B = n^{\frac{2}{3}}\) 可以达到理论最优复杂度 \(O(n ^ \frac{5}{3})\),实际我的写法 \(B\)\(150\) 左右最快。

实际上让 \(B\) 再变小还可以更快,但再小就爆空间了。

目前是跑到了最优解。

Code

#include<bits/extc++.h>
using namespace std;
#define ll long long namespace FastIO { // 太长了就省略快读快写
}
#define cin FastIO::cin
#define cout FastIO::coutconst int N=1e5+5,B=140,K=N/B+5,T=B+5;
class Node
{
public:int pos,ip,k;
};
class node
{
public:int lt,rt,k;
}e[N];
int n,q,slen[N],bl[N],res,lst[N],nxt[N];
vector<Node>p[N];
string s[N],re[N];
int f[N][T<<1],g[N][T<<1];class failtree //分块
{
public:const int B=317;
vector<int>nbr[N];
int dfn[N],ed[N],tot,R[N],sumB[N],sum[N],knum,pos[N];inline void add(int x,int y)
{nbr[x].push_back(y);return ;
}void dfs(int cur)
{dfn[cur]=++tot;for(int nxt:nbr[cur])dfs(nxt);ed[cur]=tot;return ;
}inline void init()
{for(int i=0;i<=tot;i+=B){R[++knum]=min(tot,i+B-1);sumB[knum]=0;for(int j=i;j<=R[knum];++j)pos[j]=knum,sum[j]=0;}return ;
}inline void update(int x)
{x=dfn[x];for(int i=pos[x];i<=knum;++i)sumB[i]++;for(int i=x;i<=R[pos[x]];++i)sum[i]++;return ;
}inline int query(int x)
{int lt=dfn[x],rt=ed[x],y=pos[rt];x=pos[lt];int val=(lt==(R[x-1]+1)?0:sum[lt-1]);if(x==y)return sum[rt]-val;return (sum[R[x]]-val)+(sumB[y-1]-sumB[x])+sum[rt];
}};class ACAM //一棵正着一棵反着
{
public:int Trie[N][26],fail[N],tot;
failtree flt;inline void insert(string s)
{int u=0;for(char ch:s){int t=ch-'a';if(Trie[u][t]==0)Trie[u][t]=++tot;u=Trie[u][t];}return ;
}inline void build()
{queue<int>q;q.push(0);while(q.empty()==false){int cur=q.front();q.pop();for(int i=0;i<26;++i){if(Trie[cur][i]==0)Trie[cur][i]=Trie[fail[cur]][i];else{int nxt=Trie[cur][i];if(cur!=0)fail[nxt]=Trie[fail[cur]][i];flt.add(fail[nxt],nxt);q.push(nxt);}}}flt.dfs(0);flt.init();return ;
}inline void update(string s)
{int u=0;for(char ch:s){u=Trie[u][ch-'a'];flt.update(u);}return ;
}}zdz,zdf;
int wzz[N],wzf[N],to[N];
int num[K][K][K],cn;
ll anst[N];
vector<int>sumz[K][K],sumf[K][K];
int len[N];signed main()
{cin>>n>>q;for(int i=1;i<=n;i++){cin>>s[i];len[i]=s[i].size();zdz.insert(s[i]);re[i]=s[i];reverse(re[i].begin(),re[i].end());zdf.insert(re[i]);if(len[i]>B)bl[++res]=i,slen[res]=slen[res-1]+len[i];if(len[i]>B+B){wzz[i]=0;for(int j=0;j<len[i]-B-1;++j)wzz[i]=zdz.Trie[wzz[i]][s[i][j]-'a'];wzf[i]=0;for(int j=0;j<len[i]-B-1;++j)wzf[i]=zdf.Trie[wzf[i]][re[i][j]-'a'];}}zdz.build();zdf.build();int ttt=0;for(int i=1;i<=n;++i){if(len[i]>B)ttt++;lst[i]=ttt;}ttt=res+1;for(int i=n;i>=1;--i){if(len[i]>B)ttt--;nxt[i]=ttt;}for(int i=0;i<=res;++i)for(int j=1;j<=res;++j){sumz[i][j].resize(len[bl[j]],0);sumf[i][j].resize(len[bl[j]],0);}for(int i=1;i<=res;++i){zdz.update(s[bl[i]]);zdf.update(re[bl[i]]);for(int j=1;j<=res;++j){int a=0,b=0;for(int k=0;k+1<len[bl[j]]-B;++k){a=zdz.Trie[a][s[bl[j]][k]-'a'];b=zdf.Trie[b][re[bl[j]][k]-'a'];sumz[i][j][k]=zdz.flt.query(a);sumf[i][j][k]=zdf.flt.query(b);}}}for(int i=1;i<=q;++i){int lt,rt,pos;cin>>lt>>rt>>pos;p[lt-1].push_back({pos,i,-1});p[rt].push_back({pos,i,1});e[i]=(node){lt,rt,pos};}zdz.flt.init();zdf.flt.init();for(int i=1;i<=n;i++){zdz.update(s[i]);zdf.update(re[i]);for(Node cur:p[i]){int k=cur.pos,a=0,b=0;if(len[k]<=B+B){for(int j=0;j+1<len[k];++j){a=zdz.Trie[a][s[k][j]-'a'];b=zdf.Trie[b][re[k][j]-'a'];f[cur.ip][j]+=zdz.flt.query(a)*cur.k;g[cur.ip][j]+=zdf.flt.query(b)*cur.k;}}else{for(int j=0;j<=B-1;++j){a=zdz.Trie[a][s[k][j]-'a'];b=zdf.Trie[b][re[k][j]-'a'];f[cur.ip][j]+=zdz.flt.query(a)*cur.k;g[cur.ip][j]+=zdf.flt.query(b)*cur.k;}a=wzz[k],b=wzf[k];int vw=B+B;for(int j=len[k]-B-1;j+1<len[k];++j){a=zdz.Trie[a][s[k][j]-'a'];b=zdf.Trie[b][re[k][j]-'a'];f[cur.ip][--vw]+=zdz.flt.query(a)*cur.k;g[cur.ip][vw]+=zdf.flt.query(b)*cur.k;}}}}for(int i=1;i<=q;++i){int k=e[i].k;ll sum=0;if(len[k]<=B+B){for(int j=0;j+1<len[k];++j)sum+=1ll*f[i][j]*g[i][len[k]-2-j];}else{for(int j=0;j+1<=B;++j)sum+=1ll*f[i][j]*g[i][j+B]+1ll*f[i][j+B]*g[i][j];}if(len[k]>B){int L=nxt[e[i].lt],rr=lst[e[i].rt];if(L<=rr){if(num[L][rr][lst[k]]==0){num[L][rr][lst[k]]=++cn;for(int j=B;j+1<len[k]-B;++j)anst[cn]+=1ll*(sumz[rr][lst[k]][j]-sumz[L-1][lst[k]][j])*(sumf[rr][lst[k]][len[k]-2-j]-sumf[L-1][lst[k]][len[k]-2-j]);}sum+=anst[num[L][rr][lst[k]]];}}cout<<sum<<"\n";}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/971327.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

刚刚竟然忘了质数怎么找

include<stdio.h> int main(){ int i; int cnt=0; int x=2; while(cnt<50){ int is = 1; for(i=2;i<x;i++){ if(x%i0) { is = 0; break; } } if(is1){ cnt++; printf("%d\n",x); } x++; } retur…

alisql数据库怎样提高安全性

提高MySQL数据库(通常被称为MySQL,而不是alisql,可能是个误称)的安全性是一个多方面的过程,涉及多个层面的措施。以下是一些关键的安全最佳实践: 初始安全设置安全安装:使用mysql_secure_installation命令进行安…

JSAPI Three 是什么?—— 百度地图二三维一体化渲染引擎入门指南

深入浅出地介绍百度地图 JSAPI Three(mapvthree)是什么,解释其与 mapvthree 的关系,说明目标受众和应用场景,帮助第一次接触的开发者快速理解这个突破传统地图引擎局限的二三维一体化渲染引擎。如果你正在寻找一个…

2025氮化硼陶瓷实力榜:福维科五星领衔,氮化硼陶瓷/高温绝缘体/坩埚/套管/基板/高温构件/耐腐蚀构件/微波和红外窗口制品/润滑剂、脱模剂和涂层/中子吸收材料等制品赋能工业升级

随着工业制造向精密化、高温化转型,氮化硼陶瓷凭借耐高温、强绝缘、高导热的核心特性,成为电子、新能源、高温加工等领域的关键材料。2025 年优质企业推荐榜聚焦多元应用需求,精选 4 家实力企业,其中福维科新材料科…

#题解#洛谷 P1904 天际线#离散化#

P1904 天际线 - 洛谷 分析类似染色问题,P1496 火烧赤壁 - 洛谷用x数组将所有点的横坐标离散化,用book[i]记录 [ x [ i ] ~ x [ i + 1 ] ) 的高度,(左闭右开)每次“染色”将高度更新为最大值代码实现 #include<…

关于2025沈阳打铁的二三事

关于2025沈阳打铁的二三事11.14坐飞机出发了,第一次坐飞机有些紧张和兴奋 11.15下午他们在打省赛无所事事在酒店摆烂……晚上第一次见识到打比赛的机子是这么的高级,牛牛牛 11.16比赛前特别激动,第一次打比赛已经开…

2025实力派防冻/工程装土/草袋子供应商排行榜:防汛 / 保温 / 护坡草袋子全场景覆盖,3家优质企业凭硬实力出圈

在工程建设、生态防护、物流包装等领域,草袋子作为环保且实用的天然材料,需求持续攀升。2025 年,一批专注草制品加工的企业凭借稳定品质与场景适配力脱颖而出,以下为 3 家口碑与实力兼具的草袋子供应商推荐。 一、…

2025临时/水冲储污式/太阳能/真空吸附/气压式/发泡式/打包式/景区/工地/音乐节/展会/马拉松/公园/移动厕所品牌实力榜:三大优质企业领跑多场景适配解锁便捷环保新体验

随着户外经济、大型活动与基建工程的蓬勃发展,移动厕所、临时移动厕所已成为景区、工地、音乐节、展会等场景的必备设施。2025 年行业聚焦环保化、智能化与场景化,以下三大实力企业凭借扎实品质与多元解决方案脱颖而…

2025健康饮品风向标:三大品牌领跑司鲁肽燃燃燕麦/简腩肽清清西梅/燕麦/西梅/果蔬/营养素饮品与火麻仁肽爆爆纤维/固体饮料赛道,惠植健凭多元布局登顶

2025年健康饮品市场持续升温,营养素饮品与固体饮料凭借天然、便捷的属性成为消费主流,市场规模预计突破千亿大关。本次榜单聚焦行业优质品牌,基于产品创新、品类覆盖、消费口碑等维度,精选出三大实力代表,为消费者…

Nov 20

今天依然是爆炸的一天,第一题因为使用了下标访问了 map 而导致了 tle, T1 直接 100 -> 60,觉得写一些赛后总结会好很多,最近也不想写什么题了,于是便总结总结吧。 比赛经验上的 似乎真的有 OI 教练模拟器的虎头…

CODE3:TIM定时器 - LI,Yi

TIM定时中断基本用法的示例一、定时器定时中断 1.1 main.c /* Includes ------------------------------------------------------------------*/ #include "stm32f10x.h" // Device heade…

深度学习之批量归一化的原理

Batch Normalization(批标准化)是一种深度学习中常用的技术,用于提高神经网络的训练速度和稳定性。它由 Sergey Ioffe 和 Christian Szegedy 在 2015 年的论文《Batch Normalization: Accelerating Deep Network Tr…

Spark微博舆情分析系统 情感分析 爬虫 Hadoop和Hive 贴吧资料 双平台 讲解视频 大内容 Hadoop ✅

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

LIB3:MISC固件库 - LI,Yi

介绍标准库中的MISC固件库1. misc.h * @file misc.h * @brief 本文件包含所有杂项固件库函数的函数原型(CMSIS 函数的补充功能)。1.1 变量声明 /** * @brief NVIC 初始化结构体定义 */typedef struct {uint8_…

AT_abc250_h [ABC250Ex] Trespassing Takahashi

推式子题 考虑如何求出任意一个点到离它最近的房子的距离? 有一个很巧妙的处理方法是,我们可以建一个超级源点,连向所有房子,权值设为 \(0\),然后在新图上跑一个最短路,就能求出所有点到离它最近的房子的距离 \(…

11/20

今天无事发生,明天没课,嘿嘿

Langchain Splitter源码阅读笔记(一)CharacterTextSplitter

一、TextSplitter TextSplitter继承自BaseDocumentTransformer,是一个抽象类,不能直接创建实例。核心(内部)属性有: _chunk_size: 每块大小 _chunk_overlap: 每块之间的重叠区大小 _length_function: 计算大小的方法…

《从“直接对话”到 “集成开发调用”:智谱 GLM-4.6 引领 Coding 场景的效率跃迁》 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

完整教程:Visual Studio Code 高效开发完全指南(2025年更新版)

完整教程:Visual Studio Code 高效开发完全指南(2025年更新版)2025-11-20 18:57 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !im…