「雅礼集训 2017 Day7」事情的相似度——treap启发式合并

2023-11-01 03:50

本文主要是介绍「雅礼集训 2017 Day7」事情的相似度——treap启发式合并,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目

「雅礼集训 2017 Day7」事情的相似度

题解

这题的题解是我拖得最久的一篇,我半年前就第一次做这题,到现在才来填坑。

题目问的是区间(l,r)内任两个不同前缀的最长公共后缀,也就是对于所有a\geq l,b\leq r,a<b的a、b,求max(lcs(:a,:b))lcs(:a,:b)表示前缀a和前缀b的最长公共后缀。

然后考虑把01串插入到后缀自动机里,记录一下每个前缀对应的parent树节点,那么两个前缀的最长公共后缀就是parent树上的最近公共祖先LCA的len值,或者所有公共祖先的len值取max。

所以对parent树跑一次dfs,那么一个节点的len值对子树中所有前缀两两之间都有贡献。考虑如何计算贡献:

设节点u有儿子_a、_b,_a的子树中所有前缀排序后构成数组a,b的子树中所有前缀排序后构成数组b,设u的len值为s,

那么对于每一个b_i只要贪心地找到一个a_j满足b_{i-1}<a_j<b_i,a_{j+1}>b_i,就可以对所有l\leq a_j,r\geq b_i的询问产生s的贡献,因为对于数组b的前缀之间已经算入了一个更大的贡献(即_b的len值),数组a同理,所以对l\leq b_{i-1}的询问算s的贡献肯定不优了。而由于是对所有l\leq a_j,r\geq b_i的询问产生的贡献,所以,a_j以前的贡献不用重复算,也就是只需要找到第一个小于b_ia_j即可。

我们用非旋treap(或其它平衡树也行)维护每个子树内的所有前缀,那么每找一次并记录贡献的时间是O(\log|a|)的,合并a、b数组的复杂度为O(|b|\log(|a|+|b|)),直接做肯定会被卡到O(n^2\log n),考虑启发式合并,每次选长度较小的数组为b,那么期望情况下(parent树的形态接近堆)复杂度是O(n\log^2n),而长链情况下复杂度是O(n\log n),还小一些,所以总复杂度介于O(n\log n)~O(n\log^2n)之间。

最后只要把询问离线排序然后用个线段树处理一下贡献和询问就好了,复杂度也是O(n\log n)~O(n\log^2n)

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<ctime>
#define ll long long
#define MAXN 100005
#define INF 0x3f3f3f3f
using namespace std;
inline ll read(){ll x=0;bool f=1;char s=getchar();while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();return f?x:-x;
}
int n,m,ans[MAXN],tr[MAXN*3],p;    //zkw线段树处理贡献
inline void add(int l,int r,int z){for(l=p+l-1,r=p+r+1;l^1^r;l>>=1,r>>=1){if(~l&1)tr[l^1]=max(tr[l^1],z);if(r&1)tr[r^1]=max(tr[r^1],z);}
}
inline int sch(int x){int res=0;for(x=p+x;x>0;x>>=1)res=max(res,tr[x]);return res;
}
struct node{int x,y;node(){}node(int X,int Y){x=X,y=Y;}
};
vector<node>dat[MAXN],ask[MAXN];
struct itn{    //非旋treapint ls,rs,val,siz,l,r;itn(){}itn(int a){l=r=a,val=rand()%MAXN,siz=1,ls=rs=0;}
}t[MAXN];
inline void update(int x){t[x].siz=t[t[x].ls].siz+t[t[x].rs].siz+1;t[x].l=!t[t[x].ls].l?x:t[t[x].ls].l,t[x].r=max(t[t[x].rs].r,x);
}
inline node split(int x,int k){if(!x||!k)return node(0,x);node res;if(x<=k)res=split(t[x].rs,k),t[x].rs=res.x,update(x),res.x=x;else res=split(t[x].ls,k),t[x].ls=res.y,update(x),res.y=x;return res;
}
inline int mergg(int x,int y){if(!x||!y)return x|y;int res;if(t[x].val<t[y].val)res=x,t[x].rs=mergg(t[x].rs,y),update(x);else res=y,t[y].ls=mergg(x,t[y].ls),update(y);return res;
}
struct SAM{    //后缀自动机int ch[2],fa,len;SAM(){ch[1]=ch[2]=fa=len=0;}
}sam[MAXN<<1];
int las=1,tot=1,id[MAXN<<1],root[MAXN<<1];
inline void samadd(bool c,int x){int p=las,np=las=++tot;sam[np].len=sam[p].len+1;for(;p&&!sam[p].ch[c];p=sam[p].fa)sam[p].ch[c]=np;if(!p)sam[np].fa=1;else{int q=sam[p].ch[c],nq;if(sam[q].len==sam[p].len+1)sam[np].fa=q;else{nq=++tot,sam[nq]=sam[q],sam[nq].len=sam[p].len+1,sam[q].fa=sam[np].fa=nq;for(;p&&sam[p].ch[c]==q;p=sam[p].fa)sam[p].ch[c]=nq;}}id[np]=x;
}
vector<int>G[MAXN<<1];
inline void dfs(int x){if(id[x]>0)root[x]=id[x],t[id[x]]=itn(id[x]);for(int i=0;i<G[x].size();i++){int v=G[x][i];dfs(v);int a=root[x],b=root[v];if(t[a].siz>t[b].siz)swap(a,b);node c,d;while(a){c=split(b,t[a].l),dat[t[a].l].push_back(node(t[c.x].r,sam[x].len));if(c.y)dat[t[c.y].l].push_back(node(t[a].l,sam[x].len));d=split(a,t[a].l),b=mergg(mergg(c.x,d.x),c.y),a=d.y;}root[x]=b;}
}
inline bool getb(){char s=getchar();while(s!='0'&&s!='1'&&s>0)s=getchar();return s-'0';
}
signed main()
{srand(time(0));n=read(),m=read();for(int i=1;i<=n;i++)samadd(getb(),i);for(int i=2;i<=tot;i++)G[sam[i].fa].push_back(i);dfs(1);for(p=1;p<n+2;p<<=1);for(int i=1;i<=m;i++){int l=read(),r=read();ask[r].push_back(node(l,i));}for(int i=1;i<=n;i++){    //离线处理贡献和询问for(int j=0;j<dat[i].size();j++)add(1,dat[i][j].x,dat[i][j].y);for(int j=0;j<ask[i].size();j++)ans[ask[i][j].y]=sch(ask[i][j].x);}for(int i=1;i<=m;i++)printf("%d\n",ans[i]);return 0;
}

 

这篇关于「雅礼集训 2017 Day7」事情的相似度——treap启发式合并的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/320279

相关文章

Python自动化办公之合并多个Excel

《Python自动化办公之合并多个Excel》在日常的办公自动化工作中,尤其是处理大量数据时,合并多个Excel表格是一个常见且繁琐的任务,下面小编就来为大家介绍一下如何使用Python轻松实现合... 目录为什么选择 python 自动化目标使用 Python 合并多个 Excel 文件安装所需库示例代码

Python如何计算两个不同类型列表的相似度

《Python如何计算两个不同类型列表的相似度》在编程中,经常需要比较两个列表的相似度,尤其是当这两个列表包含不同类型的元素时,下面小编就来讲讲如何使用Python计算两个不同类型列表的相似度吧... 目录摘要引言数字类型相似度欧几里得距离曼哈顿距离字符串类型相似度Levenshtein距离Jaccard相

使用Python合并 Excel单元格指定行列或单元格范围

《使用Python合并Excel单元格指定行列或单元格范围》合并Excel单元格是Excel数据处理和表格设计中的一项常用操作,本文将介绍如何通过Python合并Excel中的指定行列或单... 目录python Excel库安装Python合并Excel 中的指定行Python合并Excel 中的指定列P

基于C#实现PDF文件合并工具

《基于C#实现PDF文件合并工具》这篇文章主要为大家详细介绍了如何基于C#实现一个简单的PDF文件合并工具,文中的示例代码简洁易懂,有需要的小伙伴可以跟随小编一起学习一下... 界面主要用于发票PDF文件的合并。经常出差要报销的很有用。代码using System;using System.Col

Python视频剪辑合并操作的实现示例

《Python视频剪辑合并操作的实现示例》很多人在创作视频时都需要进行剪辑,本文主要介绍了Python视频剪辑合并操作的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录介绍安装FFmpegWindowsMACOS安装MoviePy剪切视频合并视频转换视频结论介绍

不删数据还能合并磁盘? 让电脑C盘D盘合并并保留数据的技巧

《不删数据还能合并磁盘?让电脑C盘D盘合并并保留数据的技巧》在Windows操作系统中,合并C盘和D盘是一个相对复杂的任务,尤其是当你不希望删除其中的数据时,幸运的是,有几种方法可以实现这一目标且在... 在电脑生产时,制造商常为C盘分配较小的磁盘空间,以确保软件在运行过程中不会出现磁盘空间不足的问题。但在

在C#中合并和解析相对路径方式

《在C#中合并和解析相对路径方式》Path类提供了几个用于操作文件路径的静态方法,其中包括Combine方法和GetFullPath方法,Combine方法将两个路径合并在一起,但不会解析包含相对元素... 目录C#合并和解析相对路径System.IO.Path类幸运的是总结C#合并和解析相对路径对于 C

hdu2241(二分+合并数组)

题意:判断是否存在a+b+c = x,a,b,c分别属于集合A,B,C 如果用暴力会超时,所以这里用到了数组合并,将b,c数组合并成d,d数组存的是b,c数组元素的和,然后对d数组进行二分就可以了 代码如下(附注释): #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<que

day-51 合并零之间的节点

思路 直接遍历链表即可,遇到val=0跳过,val非零则加在一起,最后返回即可 解题过程 返回链表可以有头结点,方便插入,返回head.next Code /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}*

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点