bzoj 3879 虚树
题目大意:
给一个字符串,多次询问k个后缀,求它们两两间LCP长度总和
分析:
转化为后缀树,用虚树求
注意:
后缀树中代表后缀的点都是叶子节点
题目中取模并没有卵用
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype>
using namespace std;
typedef long long LL;
const int M=1000007;
inline int rd(){
int x=0;bool f=1;char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=0;
for(;isdigit(c);c=getchar()) x=x*10+c-48;
return f?x:-x;
}
int n,m;
char s[M];
int ch[M][26];
int pre[M];
LL len[M],sz[M];
int last=1,tot=1;
int g[M],te,hd[M],td;
struct edge{int y,next;}e[M],dw[M];
int dfn[M],tdfn;
LL ans;
int ln[M<<1],pos[M],T;
int a[M<<1][23];
int que[M*3];
int st[M],cnt;
int id[M];
void addedge(int x,int y){
e[++te].y=y;
e[te].next=g[x];
g[x]=te;
}
void addlink(int x,int y){
if(x==y) return ;
dw[++td].y=y;
dw[td].next=hd[x];
hd[x]=td;
}
int newnode(int x){
++tot;
len[tot]=x;
return tot;
}
void Sam(int x){
int p,q,np,nq;
p=last;
last=np=newnode(len[last]+1);
for(;p&&!ch[p][x];p=pre[p]) ch[p][x]=np;
if(!p) pre[np]=1;
else{
q=ch[p][x];
if(len[q]==len[p]+1) pre[np]=q;
else{
nq=newnode(len[p]+1);
for(int i=0;i<26;i++) ch[nq][i]=ch[q][i];
pre[nq]=pre[q];
pre[q]=pre[np]=nq;
for(;p&&ch[p][x]==q;p=pre[p]) ch[p][x]=nq;
}
}
}
void dfs(int x){
dfn[x]=++tdfn;
a[pos[x]=++T][0]=x;
int p,y;
for(p=g[x];p;p=e[p].next){
y=e[p].y;
dfs(y);
a[++T][0]=x;
}
}
int mn(int x,int y){
return dfn[x]<dfn[y]?x:y;
}
void init(){
int i,j,l;
for(i=2;i<=T;i++) ln[i]=ln[i>>1]+1;
for(i=T;i>0;i--){
l=ln[T-i+1];
for(j=1;j<=l;j++)
a[i][j]=mn(a[i][j-1],a[i+(1<<j-1)][j-1]);
}
}
int LCA(int x,int y){
x=pos[x],y=pos[y];
if(x>y)swap(x,y);
int l=ln[y-x+1];
return mn(a[x][l],a[y-(1<<l)+1][l]);
}
bool cmp(int x,int y){return dfn[x]<dfn[y];}
void vbuild(int z){
int i,x,anc;
sort(que+1,que+z+1,cmp);
z=unique(que+1,que+z+1)-(que+1);
for(i=1;i<z;i++){
x=LCA(que[i],que[i+1]);
hd[x]=0; sz[x]=0;
}
hd[1]=0; sz[1]=0;
for(i=1;i<=z;i++){
x=que[i];
hd[x]=0; sz[x]=1;
}
td=cnt=0;
st[++cnt]=1;
for(i=1;i<=z;i++){
x=que[i];
anc=LCA(x,st[cnt]);
if(anc==st[cnt]) st[++cnt]=x;
else{
while(cnt>1 && anc==LCA(st[cnt-1],x)){
addlink(st[cnt-1],st[cnt]);
cnt--;
}
addlink(anc,st[cnt]);
st[cnt]=anc;
st[++cnt]=x;
}
}
for(i=1;i<cnt;i++) addlink(st[i],st[i+1]);
}
void dp(int x){
int p,y;
for(p=hd[x];p;p=dw[p].next){
y=dw[p].y;
dp(y);
ans+=(sz[x]*sz[y])*len[x];
sz[x]+=sz[y];
}
}
int main(){
int i,z;
n=rd();m=rd();
scanf("%s",s+1);
for(i=n;i>0;i--){
Sam(s[i]-'a');
id[i]=last;
}
for(i=2;i<=tot;i++) addedge(pre[i],i);
dfs(1);
init();
while(m--){
z=rd();
for(i=1;i<=z;i++) que[i]=id[rd()];
vbuild(z);
ans=0;
dp(1);
printf("%lld\n",ans);
}
return 0;
}
bzoj 3879 虚树的更多相关文章
- bzoj 2286(虚树+树形dp) 虚树模板
树链求并又不会写,学了一发虚树,再也不虚啦~ 2286: [Sdoi2011]消耗战 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 5002 Sol ...
- BZOJ 3879: SvT [虚树 后缀树]
传送门 题意: 多次询问,给出一些后缀,求两两之间$LCP$之和 哈哈哈哈哈哈哈竟然$1A$了,刚才还在想如果写不好这道题下节数学就不上了,看来是上天让我上数学课啊 $Suffix\ Virtual\ ...
- BZOJ 3879: SvT 虚树 + 后缀自动机
Description (我并不想告诉你题目名字是什么鬼) 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在S中出现的起始 ...
- BZOJ.5287.[AHOI HNOI2018]毒瘤(虚树 树形DP)
BZOJ LOJ 洛谷 设\(f[i][0/1]\)表示到第\(i\)个点,不选/选这个点的方案数.对于一棵树,有:\[f[x][0]=\prod_{v\in son[x]}(f[v][0]+f[v] ...
- BZOJ.2286.[SDOI2011]消耗战(虚树 树形DP)
题目链接 BZOJ 洛谷P2495 树形DP,对于每棵子树要么逐个删除其中要删除的边,要么直接断连向父节点的边. 如果当前点需要删除,那么直接断不需要再管子树. 复杂度O(m*n). 对于两个要删除的 ...
- Bzoj 2286 & Luogu P2495 消耗战(LCA+虚树+欧拉序)
题面 洛谷 Bzoj 题解 很容易想到$O(nk)$的树形$dp$吧,设$f[i]$表示处理完这$i$颗子树的最小花费,同时再设一个$mi[i]$表示$i$到根节点$1$路径上的距离最小值.于是有: ...
- BZOJ 3572 [HNOI2014]世界树 (虚树+DP)
题面:BZOJ传送门 洛谷传送门 题目大意:略 细节贼多的虚树$DP$ 先考虑只有一次询问的情况 一个节点$x$可能被它子树内的一个到x距离最小的特殊点管辖,还可能被管辖fa[x]的特殊点管辖 跑两次 ...
- BZOJ 2286 消耗战 (虚树+树形DP)
给出一个n节点的无向树,每条边都有一个边权,给出m个询问,每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接.最少的边权和是多少.(n<=250000,sigma(ki)<= ...
- BZOJ 2286 树链剖分+DFS序+虚树+树形DP
第一次学习虚树,就是把无关的点去掉.S里维护一条链即可. #include <iostream> #include <cstring> #include <cstdio& ...
随机推荐
- document.createDocumentFragment 方法
基本概念 document.createDocumentFragment 方法会创建一个 DocumentFragment 对象,该对象是一个存在于 DOM 树之外的 DOM 节点.它有一个非常有用的 ...
- sscanf用法
sscanf与scanf类似,都是用于输入的,只是后者以键盘(stdin)为输入源,前者以固定字符串为输入源. 1. 常见用法. 1 2 3 char buf[512] ; sscanf(" ...
- 特殊字符 js处理
2.特殊字符传递过程中的处理 (1)js页面的处理 var url= "#@+&这些带有特殊字符"; url=encodeURI(encodeURI(url));//转码两 ...
- json解析尖括号<>
如题 rs.getString("HEADLINE").replaceAll("<", " <").replaceAll(&qu ...
- 根据View获取该控制器
//根据View获取控制器 - (UIViewController*)viewController { for (UIView* next = [self superview]; next; next ...
- Oracle where 0=1 or 1=1
本文转载自:http://www.cnblogs.com/junyuz/archive/2011/03/10/1979646.html sql where 1=1和 0=1 的作用 where 1 ...
- Inno Setup入门(十九)——Inno Setup类参考(5)
: Install Setup 2013-02-02 11:29 377人阅读 评论(0) 收藏 举报 单选按钮 单选按钮在安装中也很常见,例如同一个程序可以选择安装不同的性质的功能,例如选择32位或 ...
- 巧妙使用Contains()方法查找一个数是否在某堆数中
问题:要判断用户输入的一个数,或者是程序里方法的一个参数的值,或者是一个变量的值是否在某堆数中. 简洁写法:把这堆数放在list中,使用list的Contains()方法检查list是否包含这个数,取 ...
- 虚拟机centos分区
在计算机上安装 Linux 系统,对硬盘进行分区是一个非常重要的步骤,下面介绍几个分区方案. 方案 1 / :建议大小在 5GB 以上. swap :即交换分区,建议大小是物理内存的 1~2 倍. 方 ...
- opencv-----基本数据类型
OpenCV提供了多种基本数据类型.可以在"…/OpenCV/cxcore/include"目录下的cxtypes.h文件中查看其详细定义. CvPoint是一个包含integer ...