Description

Link.

给定字符串,正整数集合 \(A,B\),满足 \(\forall u\in A,v\in B,1\le u,v\le n\)。

求 \(\sum_{i\in A}\sum_{j\in B}\text{LCP}(A,B)\)。

Solution

双倍经验是 SvT,只不过 SvT 这屑玩意儿卡常。

先反转串,然后插入 SAM。众所周知

把字符串反转后插入 SAM 后,两个原串的后缀在 parent tree 上的 \(\text{LCA}\) 是这两个后缀的 \(\text{LCP}\)。

然后你就可以搞两个 DP,分别跑 \(A\) 子树大小,\(B\) 子树大小。

注意根节点需要特殊处理,因为我们是跨子树跑的 DP。不过 SvT 不需要,不知道是不是我的问题(应该就是)。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n,m,dfn[500010],fa[500010][21],dep[500010],sjc,pos[200010],onepower[500010],anopower[500010],onef[500010],anof[500010];
char s[200010];
LL ans;
struct SuffixAutomaton
{
#define ID(c) ((c)-'a')
vector<int> e[500010];
int n,cntot,las,len[500010],pre[500010],ch[500010][26];
char s[200010];
void init(int _n,char c[])
{
n=_n;
for(int i=1;i<=n;++i) s[i]=c[i];
cntot=las=1;
}
void extend(char c)
{
int cur=++cntot,one=las,ano=0;
len[cur]=len[las]+1,las=cur;
while(one&&!ch[one][ID(c)]) ch[one][ID(c)]=cur,one=pre[one];
if(one==0) pre[cur]=1;
else
{
ano=ch[one][ID(c)];
if(len[one]+1==len[ano]) pre[cur]=ano;
else
{
int clone=++cntot;
len[clone]=len[one]+1;
pre[clone]=pre[ano];
memcpy(ch[clone],ch[ano],sizeof(ch[ano]));
while(one&&ch[one][ID(c)]==ano) ch[one][ID(c)]=clone,one=pre[one];
pre[ano]=pre[cur]=clone;
}
}
}
void build()
{
for(int i=1;i<=n;++i) extend(s[i]),pos[i]=las;
for(int i=2;i<=cntot;++i) e[pre[i]].emplace_back(i);
}
}SAM;
void dfs(int x,int las)
{
dfn[x]=++sjc,fa[x][0]=las,dep[x]=dep[las]+1;
for(int i=1;i^21;++i) fa[x][i]=fa[fa[x][i-1]][i-1];
for(int y : SAM.e[x]) dfs(y,x);
}
int LCA(int one,int ano)
{
if(dep[one]<dep[ano]) swap(one,ano);
for(int i=20;~i;--i) if(dep[fa[one][i]]>=dep[ano]) one=fa[one][i];
if(one^ano)
{
for(int i=20;~i;--i) if(fa[one][i]^fa[ano][i]) one=fa[one][i],ano=fa[ano][i];
return fa[one][0];
}
else return one;
}
bool cmp(int one,int ano){return dfn[one]<dfn[ano];}
struct VirtualTree
{
vector<int> e[500010];
vector<int> build(vector<int> poi)
{
sort(poi.begin(),poi.end(),cmp);
poi.erase(unique(poi.begin(),poi.end()),poi.end());
int len=poi.size();
for(int i=1;i<len;++i) poi.push_back(LCA(poi[i-1],poi[i]));
sort(poi.begin(),poi.end(),cmp);
poi.erase(unique(poi.begin(),poi.end()),poi.end());
len=poi.size();
for(int i=1;i<len;++i) e[LCA(poi[i-1],poi[i])].push_back(poi[i]);
return poi;
}
}VRT;
template<class T>
void read(T &hhh)
{
T x=0,f=1;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^'0'),c=getchar();
if(~f) hhh=x;
else hhh=-x;
}
template<class T>
void write(T x,char las='\n')
{
static int st[100],top=0;
if(x<0) putchar('-'),x=-x;
do st[++top]=x%10,x/=10; while(x);
while(top) putchar(st[top--]^'0');
putchar(las);
}
void exdfs(int x)
{
for(int y : VRT.e[x]) exdfs(y),onef[x]+=onef[y],anof[x]+=anof[y];
for(int y : VRT.e[x]) ans+=(LL)SAM.len[x]*(onef[x]-onef[y])*anof[y];
ans+=(LL)((onepower[x]&anopower[x])+onepower[x]*anof[x]+anopower[x]*onef[x])*SAM.len[x];
onef[x]+=onepower[x],anof[x]+=anopower[x];
}
int main()
{
read(n),read(m);
scanf("%s",s+1);
reverse(s+1,s+n+1);
SAM.init(n,s),SAM.build();
dfs(1,0);
while(m--)
{
int ones,anos,x;
read(ones),read(anos);
vector<int> key,tmp;
while(ones--) read(x),key.push_back(pos[n-x+1]),onepower[pos[n-x+1]]=1;
while(anos--) read(x),key.push_back(pos[n-x+1]),anopower[pos[n-x+1]]=1;
tmp=VRT.build(key);
ans=0,exdfs(tmp[0]);
write(ans);
for(int now : tmp) onef[now]=anof[now]=0,VRT.e[now].clear(),onepower[now]=anopower[now]=0;
}
return 0;
}

Solution -「CF 1073G」Yet Another LCP Problem的更多相关文章

  1. Solution -「CF 1342E」Placing Rooks

    \(\mathcal{Description}\)   Link.   在一个 \(n\times n\) 的国际象棋棋盘上摆 \(n\) 个车,求满足: 所有格子都可以被攻击到. 恰好存在 \(k\ ...

  2. Solution -「CF 1622F」Quadratic Set

    \(\mathscr{Description}\)   Link.   求 \(S\subseteq\{1,2,\dots,n\}\),使得 \(\prod_{i\in S}i\) 是完全平方数,并最 ...

  3. Solution -「CF 923F」Public Service

    \(\mathscr{Description}\)   Link.   给定两棵含 \(n\) 个结点的树 \(T_1=(V_1,E_1),T_2=(V_2,E_2)\),求一个双射 \(\varph ...

  4. Solution -「CF 923E」Perpetual Subtraction

    \(\mathcal{Description}\)   Link.   有一个整数 \(x\in[0,n]\),初始时以 \(p_i\) 的概率取值 \(i\).进行 \(m\) 轮变换,每次均匀随机 ...

  5. Solution -「CF 1586F」Defender of Childhood Dreams

    \(\mathcal{Description}\)   Link.   定义有向图 \(G=(V,E)\),\(|V|=n\),\(\lang u,v\rang \in E \Leftrightarr ...

  6. Solution -「CF 1237E」Balanced Binary Search Trees

    \(\mathcal{Description}\)   Link.   定义棵点权为 \(1\sim n\) 的二叉搜索树 \(T\) 是 好树,当且仅当: 除去最深的所有叶子后,\(T\) 是满的: ...

  7. Solution -「CF 623E」Transforming Sequence

    题目 题意简述   link.   有一个 \(n\) 个元素的集合,你需要进行 \(m\) 次操作.每次操作选择集合的一个非空子集,要求该集合不是已选集合的并的子集.求操作的方案数,对 \(10^9 ...

  8. Solution -「CF 1023F」Mobile Phone Network

    \(\mathcal{Description}\)   Link.   有一个 \(n\) 个结点的图,并给定 \(m_1\) 条无向带权黑边,\(m_2\) 条无向无权白边.你需要为每条白边指定边权 ...

  9. Solution -「CF 599E」Sandy and Nuts

    \(\mathcal{Description}\)   Link.   指定一棵大小为 \(n\),以 \(1\) 为根的有根树的 \(m\) 对邻接关系与 \(q\) 组 \(\text{LCA}\ ...

  10. Solution -「CF 487E」Tourists

    \(\mathcal{Description}\)   Link.   维护一个 \(n\) 个点 \(m\) 条边的简单无向连通图,点有点权.\(q\) 次操作: 修改单点点权. 询问两点所有可能路 ...

随机推荐

  1. 可视化生信分析利器 Galaxy 之 Docker 开发

    1. 背景 我们常常会基于某个 image 来启动一个 container,在这个 container 中我们可能会执行某些操作,比如创建一个文件,但是当这个 container 退出以后,如果我们以 ...

  2. ssh,socat端口转发

    ssh隧道 我们将要研究的第一个协议是SSH,因为它已经内置了通过SSH隧道进行端口转发的功能.虽然SSH曾经是与Linux系统相关联的协议,但现在Windows默认安装了OpenSSH客户端,因此您 ...

  3. 2023-06-14:我们从二叉树的根节点 root 开始进行深度优先搜索。 在遍历中的每个节点处,我们输出 D 条短划线(其中 D 是该节点的深度) 然后输出该节点的值。(如果节点的深度为 D,则其

    2023-06-14:我们从二叉树的根节点 root 开始进行深度优先搜索. 在遍历中的每个节点处,我们输出 D 条短划线(其中 D 是该节点的深度) 然后输出该节点的值.(如果节点的深度为 D,则其 ...

  4. @Document元注解的使用

    @Documented注解标记的元素,Javadoc工具会将此注解标记元素的注解信息包含在javadoc中.默认,注解信息不会包含在Javadoc中.示例如下: 声明Book注解,并使用@Docume ...

  5. MultiscaleResNet50:AnEfficientandAccurateApproachforIma

    目录 标题:<51. Multi-scale ResNet-50: An Efficient and Accurate Approach for Image Recognition> 背景 ...

  6. Fabric架构详解

    1 整体架构 2 运行架构 Fabric CA(可选) peer:主节点模块,负责存储区块链数据,运行维护链码 orderer:交易打包,排序模块 cryptogen:组织和证书等资料生成模块 con ...

  7. GO 语言中 slice 的理解

    GO 语言中 slice 理解 为什么说 Go 语言的 slice 是引用类型,其底层实现明明是一个结构体? slice 的底层实现是一个包含三个字段的结构体:指向底层数组的指针.slice 的长度和 ...

  8. decode php解密代码,方便好用,请收藏

    <?php //已经加密的文件内容 $a = "eval(gzinflate(base64_decode('tVRNb+IwEL3vr/AhwomU5WOPVHSF2lSg7QJK0j ...

  9. 自动化SQL注入工具——Sqlmap

    Sqlmap – 简介 Sqlmap是一个自动化检测和利用SQL注入漏洞的免费开源工具 1.支持对多种数据库进行注入测试,能够自动识别数据库类型并注入 2.支持多种注入技术,并且能够自动探测使用合适的 ...

  10. 【Azure Event Hub】自定义告警(Alert Rule)用来提示Event Hub的消息incoming(生产)与outgoing(消费)的异常情况

    问题描述 在使用Azure Service Bus的时候,我们可以根据Queue中目前存在的消息数来判断当前消息是否有积压的情况. 但是,在Event Hub中,因为所有消息都会被存留到预先设定的保留 ...