SAM+虚树--CF1073G Yet Another LCP Problem

2024-01-03 13:58

本文主要是介绍SAM+虚树--CF1073G Yet Another LCP Problem,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

传送门

这题调的我两眼发黑。。。

首先想 S A M SAM SAM建出来就是反串的后缀树,那么把原串反转一下,求后缀的 l c p lcp lcp就变成了求前缀的最长后缀,在 S A M SAM SAM上就是两个代表节点 l c a lca lca l e n len len,用这些关键点和他们的 l c a lca lca建出虚树,然后在虚树上跑,设 s i z a [ u ] , s i z b [ u ] siz_a[u],siz_b[u] siza[u],sizb[u]分别表示 u u u子树内 a , b a,b a,b节点的个数,则 a n s + = s i z a [ u ] ∗ s i z b [ u ] ∗ ( l u − l f a ) ans+=siz_a[u]*siz_b[u]*(l_u-l_{fa}) ans+=siza[u]sizb[u](lulfa)

然后就是虚树的基本操作,把关键点按 d f n dfn dfn排序,两两求 l c a lca lca加入关键点中,然后再排序后建虚树,注意建树弹栈的时候要判断当前点是不是这个点的祖先,要用 l c a lca lca判断,还有清空的时候不能遍历 c n t cnt cnt不然就 t l e tle tle了,还有数组要开一倍

细节非常多了可以说
像我这样码力弱的一点一点调过来已经写的不成样子了,将就着看一眼代码吧

(本来打开这题是想做一下虚树结果发现 S A M SAM SAM全忘了于是从头开始学 S A M SAM SAM。。。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define N 400005
#define LL long long
using namespace std;template<class T>inline void rd(T &x){x=0; short f=1; char c=getchar();while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();x*=f;
}int n,q,id[N],sza,szb,qu[N],tot,rt,siz1[N],siz2[N],ty1[N],ty2[N];
int cnt,head[N],nxt[N<<1],to[N<<1],dep[N],f[N][20],dfn[N],num;
char s[N];
bool vis[N];
LL ans;
vector<int> vec[N];struct qwq{int pos,key;qwq(const int pp=0,const int kk=0){pos=pp,key=kk;}inline bool operator <(const qwq &x) const{return key<x.key;}
}a[N<<1];inline void add(int x,int y){to[++cnt]=y,nxt[cnt]=head[x],head[x]=cnt;to[++cnt]=x,nxt[cnt]=head[y],head[y]=cnt;
}struct SAM{int lst,cnt,ch[N<<1][26],fa[N<<1],l[N<<1];inline void insert(int c,int pos){int p=lst,np=++cnt; id[pos]=cnt; lst=np; l[np]=l[p]+1;while(p&&!ch[p][c]) ch[p][c]=np,p=fa[p];if(!p) fa[np]=1;else{int q=ch[p][c];if(l[p]+1==l[q]) fa[np]=q;else{int nq=++cnt; l[nq]=l[p]+1;memcpy(ch[nq],ch[q],sizeof ch[q]);fa[nq]=fa[q],fa[q]=fa[np]=nq;while(ch[p][c]==q) ch[p][c]=nq,p=fa[p];}}}inline void build(){scanf("%s",s+1); int len=strlen(s+1);lst=cnt=1; for(int i=len;i;i--) insert(s[i]-'a',i);for(int i=1;i<=cnt;i++)if(fa[i]) add(fa[i],i);}
}sam;void dfs(int u,int fa){dfn[u]=++num;for(int i=head[u],v;i;i=nxt[i])if((v=to[i])!=fa){dep[v]=dep[u]+1; f[v][0]=u;for(int j=1;j<=18;j++)f[v][j]=f[f[v][j-1]][j-1];dfs(v,u);}
}inline int LCA(int x,int y){if(dep[x]<dep[y]) swap(x,y);for(int i=18;i>=0;i--)if(dep[f[x][i]]>=dep[y]) x=f[x][i];if(x==y) return x;for(int i=18;i>=0;i--)if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];return f[x][0];
}inline void init(){for(int i=1;i<=sam.cnt;i++) vec[i].clear(),vis[i]=0,ty1[i]=ty2[i]=0; tot=0;
}void dfs2(int u,int fa){siz1[u]=ty1[u],siz2[u]=ty2[u];for(int i=0;i<vec[u].size();i++) dfs2(vec[u][i],u),siz1[u]+=siz1[vec[u][i]],siz2[u]+=siz2[vec[u][i]];ans+=1LL*siz1[u]*siz2[u]*(sam.l[u]-sam.l[fa]);
}inline LL solve(){ans=0; int pt=tot,now=0;for(int i=1;i<pt;i++){int z=LCA(a[i].pos,a[i+1].pos);if(!vis[z]) vis[z]=1,a[++tot]=qwq(z,dfn[z]);}sort(a+1,a+tot+1); qu[++now]=rt=a[1].pos;for(int i=2;i<=tot;i++){if(a[i].pos==a[i-1].pos) continue;while(now>1 && LCA(qu[now],a[i].pos)!=qu[now]) now--;vec[qu[now]].push_back(a[i].pos); qu[++now]=a[i].pos;}dfs2(rt,0); return ans;
}int main(){rd(n); rd(q);sam.build(); dfs(1,0);while(q--){rd(sza),rd(szb); int x;for(int i=1;i<=sza;i++){rd(x); a[++tot]=qwq(id[x],dfn[id[x]]);vis[id[x]]=1; ty1[id[x]]=1;}for(int i=1;i<=szb;i++){rd(x); if(!vis[id[x]]) a[++tot]=qwq(id[x],dfn[id[x]]);vis[id[x]]=1; ty2[id[x]]=1;}sort(a+1,a+tot+1);printf("%I64d\n",solve());if(q){for(int i=1;i<=tot;i++) x=a[i].pos,vec[x].clear(),vis[x]=0,ty1[x]=ty2[x]=0;tot=0;}}return 0;
}

这篇关于SAM+虚树--CF1073G Yet Another LCP Problem的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

uva 10025 The ? 1 ? 2 ? ... ? n = k problem(数学)

题意是    ?  1  ?  2  ?  ...  ?  n = k 式子中给k,? 处可以填 + 也可以填 - ,问最小满足条件的n。 e.g k = 12  - 1 + 2 + 3 + 4 + 5 + 6 - 7 = 12 with n = 7。 先给证明,令 S(n) = 1 + 2 + 3 + 4 + 5 + .... + n 暴搜n,搜出当 S(n) >=

【LVI-SAM】激光雷达点云处理特征提取LIO-SAM 之FeatureExtraction实现细节

激光雷达点云处理特征提取LIO-SAM 之FeatureExtraction实现细节 1. 特征提取实现过程总结1.0 特征提取过程小结1.1 类 `FeatureExtraction` 的整体结构与作用1.2 详细特征提取的过程1. 平滑度计算(`calculateSmoothness()`)2. 标记遮挡点(`markOccludedPoints()`)3. 特征提取(`extractF

LCP 485. 最大连续 1 的个数[lleetcode -11]

从今天起,我们的算法开始研究搜索,首先就是DFS深度优先搜索(depth-first seach,DFS)在搜索到一个新的节点时,立即对该新节点进行遍 历;因此遍历需要用先入后出的栈来实现,也可以通过与栈等价的递归来实现。对于树结构而言, 由于总是对新节点调用遍历,因此看起来是向着“深”的方向前进。 下面是一个一维的DFS算法 LCP 485. 最大连续 1 的个数 给定一个二进制数组 nu

11991 - Easy Problem from Rujia Liu?

题意: 输入一串整型数列,再输入两个数k,v,输出第k个v的序号。不存在则输出0,如第一个样例 8 41 3 2 2 4 3 2 11 3 //第1个3,序号为2,输出22 4 //第2个4,不存在,输出03 2 //第3个2,序号为7,输出74 2 思路: struct num {

HDU 1016 Prime Ring Problem (深搜)

OJ题目 : click here ~~ 大概题意:n个数,形成一个环,使得相邻两个数的和为素数。以1开始,按字典序输出序列。 很简单的深搜。 AC_CODE int n;int visit[22];int num[22];int len;bool Is_prime(int x){for(int i = 2;i*i <= x;i++)if(x%i == 0) return

LVM 'Can’t open /dev/sdb1 exclusively. Mounted filesystem?' Problem

在将几块盘做LVM时,遇到一个之前都没遇到过的问题: root@ubuntu:~# pvcreate /dev/sdc1Can't open /dev/sdc1 exclusively. Mounted filesystem? 首先第一反应就是查看这个分区是否已经在使用了,但是没有。 查看硬盘的一些信息: root@ubuntu:~# cat /proc/partitionsmajo

Segment Anything Model(SAM)中的Adapter是什么?

在META团队发布的Segment Anything Model (SAM) 中,Adapter 是一种用于提升模型在特定任务或领域上的性能的机制。具体来说,SAM 是一个通用的分割模型,能够处理多种不同类型的图像分割任务,而 Adapter 的引入是为了更好地让模型适应不同的任务需求。 Adapter 的主要功能是: 模块化设计:Adapter 是一种小规模的、可插拔的网络模块,可以在不改

树链剖分 + 后缀数组 - E. Misha and LCP on Tree

E. Misha and LCP on Tree  Problem's Link   Mean:  给出一棵树,每个结点上有一个字母。每个询问给出两个路径,问这两个路径的串的最长公共前缀。 analyse: 做法:树链剖分+后缀数组. 记录每条链的串,正反都需要标记,组成一个长串. 然后记录每条链对应的串在大串中的位置,对大串求后缀数组,最后询问就是在一些链

【HDU】3861 The King’s Problem 强连通缩点+有向图最小路径覆盖

传送门:【HDU】3861 The King’s Problem 题目分析:首先强连通缩点,因为形成一个环的王国肯定在一条路径中,这样才能保证拆的少。 然后缩点后就是DAG图了,由于题目要求的是最小路径覆盖,那么二分匹配即可。 代码如下: #include <cstdio>#include <cstring>#include <algorithm>#includ