BZOJ 3879: SvT 虚树 + 后缀自动机
Description
(我并不想告诉你题目名字是什么鬼)
有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n].
现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在S中出现的起始位置来表示),求这些后缀两两之间的LCP(LongestCommonPrefix)的长度之和.一对后缀之间的LCP长度仅统计一遍.
Input
第一行两个正整数n,m,分别表示S的长度以及询问的次数.
接下来一行有一个字符串S.
接下来有m组询问,对于每一组询问,均按照以下格式在一行内给出:
首先是一个整数t,表示共有多少个后缀.接下来t个整数分别表示t个后缀在字符串S中的出现位置.
Output
题解:和CF的一个题几乎一模一样,都是利用虚树统计答案
#include <bits/stdc++.h>
#define setIO(s) freopen(s".in","r",stdin) , freopen(s".out","w",stdout)
#define maxn 1200004
#define mod 23333333333333333
#define ll long long
using namespace std;
int edges,n,Q;
int hd[maxn],to[maxn],nex[maxn],tr[maxn];
char str[maxn];
void addedge(int u,int v,int c) //1001 个
{
nex[++edges]=hd[u],hd[u]=edges,to[edges]=v;
}
namespace SAM
{
int last, tot;
int len[maxn<<1],f[maxn<<1],trans[maxn<<1][27];
void init() { last=tot=1; }
int extend(int c)
{
int np=++tot,p=last;
len[np]=len[p]+1,last=np;
while(p&&!trans[p][c]) trans[p][c]=np, p=f[p];
if(!p) f[np]=1;
else
{
int q=trans[p][c];
if(len[q]==len[p]+1) f[np]=q;
else
{
int nq=++tot;
len[nq]=len[p]+1;
memcpy(trans[nq],trans[q],sizeof(trans[q]));
f[nq]=f[q],f[q]=f[np]=nq;
while(p&&trans[p][c]==q) trans[p][c]=nq,p=f[p];
}
}
return np;
}
void build()
{
for(int i=2;i<=tot;++i) addedge(f[i],i,len[f[i]]-len[i]);
}
}
int tim;
int dfn[maxn],Top[maxn],hson[maxn],siz[maxn],fa[maxn],dis[maxn];
void dfs1(int u,int ff)
{
fa[u]=ff,siz[u]=1,dfn[u]=++tim,dis[u]=dis[ff]+1;
for(int i=hd[u];i;i=nex[i])
{
int v=to[i];
if(v==ff) continue;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[hson[u]]) hson[u]=v;
}
}
void dfs2(int u,int tp)
{
Top[u]=tp;
if(hson[u]) dfs2(hson[u],tp);
for(int i=hd[u];i;i=nex[i])
{
int v=to[i];
if(v==fa[u]||v==hson[u]) continue;
dfs2(v,v);
}
}
int LCA(int x,int y)
{
while(Top[x]!=Top[y])
{
dis[Top[x]]>dis[Top[y]]?x=fa[Top[x]]:y=fa[Top[y]];
}
return dis[x]<dis[y]?x:y;
}
vector<int>G[maxn<<1];
ll ans=0;
int t=0;
int A[maxn<<1],mk[maxn<<1],size[maxn<<1],S[maxn<<1];
bool cmp(int i,int j)
{
return dfn[i]<dfn[j];
}
void addvir(int x,int y)
{
G[x].push_back(y);
}
void insert(int x)
{
if(t<=1) { S[++t]=x; return; }
int lca=LCA(S[t],x);
if(lca==S[t]) { S[++t]=x; return; }
while(t>1&&dis[S[t-1]]>=dis[lca]) addvir(S[t-1],S[t]),--t;
if(S[t]!=lca) addvir(lca,S[t]),S[t]=lca;
S[++t]=x;
}
void DP(int x)
{
size[x]=mk[x];
for(int i=0;i<G[x].size();++i)
{
DP(G[x][i]);
ans+=1ll*size[x]*size[G[x][i]]%mod*SAM::len[x]%mod;
ans%=mod;
size[x]+=size[G[x][i]];
}
G[x].clear();
mk[x]=0;
}
void work()
{
int k,a;
scanf("%d",&k);
for(int i=1;i<=k;++i) scanf("%d",&a), A[i]=tr[a], mk[A[i]]=1;
sort(A+1,A+1+k,cmp);
k=unique(A+1,A+1+k)-(A+1);
t=ans=0;
if(A[1]!=1) S[++t]=1;
for(int i=1;i<=k;++i) insert(A[i]);
while(t>1) addvir(S[t-1],S[t]),--t;
DP(1);
printf("%lld\n",ans);
}
int main()
{
// setIO("input");
scanf("%d%d%s",&n,&Q,str+1);
SAM::init();
for(int i=n;i>=1;--i) tr[i]=SAM::extend(str[i]-'a');
SAM::build();
dis[1]=1,dfs1(1,0),dfs2(1,1);
for(int i=1;i<=Q;++i)
{
work();
}
return 0;
}
BZOJ 3879: SvT 虚树 + 后缀自动机的更多相关文章
- BZOJ 3879: SvT [虚树 后缀树]
传送门 题意: 多次询问,给出一些后缀,求两两之间$LCP$之和 哈哈哈哈哈哈哈竟然$1A$了,刚才还在想如果写不好这道题下节数学就不上了,看来是上天让我上数学课啊 $Suffix\ Virtual\ ...
- 回文树&后缀自动机&后缀数组
KMP,扩展KMP和Manacher就不写了,感觉没多大意思. 之前感觉后缀自动机简直可以解决一切,所以不怎么写后缀数组. 马拉车主要是通过对称中心解决问题,有的时候要通过回文串的边界解决问题 ...
- [HEOI2016/TJOI2016]字符串(后缀数组+二分+主席树/后缀自动机+倍增+线段树合并)
后缀数组解法: 先二分最长前缀长度 \(len\),然后从 \(rnk[c]\) 向左右二分 \(l\) 和 \(r\) 使 \([l,r]\) 的 \(height\geq len\),然后在主席树 ...
- 【BZOJ-4556】字符串 后缀数组+二分+主席树 / 后缀自动机+线段树合并+二分
4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 657 Solved: 274[Su ...
- HackerRank Special Substrings 回文树+后缀自动机+set
传送门 既然要求对每个前缀都求出答案,不难想到应该用回文树求出所有本质不同的回文子串. 然后考虑如何对这些回文子串的前缀进行去重. 结论:答案等于所有本质不同的回文子串长之和减去字典序相邻的回文子串的 ...
- BZOJ 3238 [Ahoi2013]差异(后缀自动机)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3238 [题目大意] 给出一个串,设T[i]表示从第i位开始的后缀, 求sum(len( ...
- bzoj 3879: SvT
Description (我并不想告诉你题目名字是什么鬼) 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在S中出现的起始 ...
- HDU - 6704 K-th occurrence (后缀数组+主席树/后缀自动机+线段树合并+倍增)
题意:给你一个长度为n的字符串和m组询问,每组询问给出l,r,k,求s[l,r]的第k次出现的左端点. 解法一: 求出后缀数组,按照排名建主席树,对于每组询问二分或倍增找出主席树上所对应的的左右端点, ...
- BZOJ 2286 消耗战 (虚树+树形DP)
给出一个n节点的无向树,每条边都有一个边权,给出m个询问,每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接.最少的边权和是多少.(n<=250000,sigma(ki)<= ...
随机推荐
- 006-unity3d GUI初识、贴图、自定义鼠标指针
一.gui概念 无论摄像机拍摄到的图像怎么变换,GUI永远显示在屏幕上,不受变形.碰撞.光照的影响.对话框.战斗值.能量等.示例:用手机录像,摄像的参数不会随着拍摄场景变换.GUI基础GUI部分是每帧 ...
- 如何实现免登陆功能(cookie session?)
Cookie的机制 Cookie是浏览器(User Agent)访问一些网站后,这些网站存放在客户端的一组数据,用于使网站等跟踪用户,实现用户自定义功能. Cookie的Domain和Path属性标识 ...
- 浅谈数学上的矩阵——矩阵的乘法运算的概念及C++上的实现模板
首先让我们来谈一谈数学意义上的矩阵(在座各位也可以简单地将它理解为一个二维数组) 这样可以帮助我们理解矩阵加速及其运用的原理(矩阵加速是一个及其玄学的东西,所以请重点理解矩阵乘法) 这里给出一段严格 ...
- [Python3 填坑] 016 对 __getattr__ 和 __setattr__ 举例
目录 1. print( 坑的信息 ) 2. 开始填坑 2.1 __getattr__ 2.2 __setattr__ 1. print( 坑的信息 ) 挖坑时间:2019/04/07 明细 坑的编码 ...
- Flash存储模块调试
Flash存储模块 Flash存储模块之前SPI一直读deviceID有问题原因如下: 用正点原子的例程是可以的,但是转移到自己的工程项目里就不行!!原因是正点原子没有在SPI初始化里 RCC ...
- Java设计模式——模板方法设计模式(abstract修饰)
解释: 一个抽象类中,有一个主方法,再定义 1...n 个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用. 解决的问题: 当功能内部一 ...
- [2019杭电多校第六场][hdu6635]Nonsense Time
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6635 题意是说一开始所有数都冻结,第i秒会解冻第ki个数,求每秒状态下的最长上上升子序列长度. 这种题 ...
- Django 数据库多字段同时关联一个表为外键的解决办法
多个字段对应一个外键时,会报错,如: class Storehouse_list(models.Model):#库位列表 nid = models.AutoField(primary_key=True ...
- Mysql共享锁、排他锁、悲观锁、乐观锁
一.相关名词 |--表级锁(锁定整个表) |--页级锁(锁定一页) |--行级锁(锁定一行) |--共享锁(S锁,MyISAM 叫做读锁) |--排他锁(X锁,MyISAM 叫做写锁) |--间隙锁( ...
- vue 跳转页面返回时tab状态有误的解决办法
一.前言 最近在做新vue项目的时候遇到了一个问题,就是tab间的切换没有问题,当跳转到其他页面时,且这个页面并非子路由,再用浏览器的返回按钮返回首页时,tab的active始终指向默认的第一个选项. ...