Description

给定一张包含N个单词的表,每个单词有个价值W。要求从中选出一个子序列使得其
中的每个单词是后一个单词的子串,最大化子序列中W的和。

Input

第一行一个整数TEST,表示数据组数。 
接下来TEST组数据,每组数据第一行为一个整数N。 
接下来N行,每行为一个字符串和一个整数W。

Output

TEST行,每行一个整数,表示W的和的最大值。 
 
 
数据规模 
设字符串的总长度为Len 
30.的数据满足,TEST≤5,N≤500,Len≤10^4 
100.的数据满足,TEST≤10,N≤20000,Len≤3*10^5

题解:

感觉很多 AC 自动机的套路都是将 $trie$ 和 $fail$ 树结合,然后在 $fail$ 树上维护一些东西.
对于本题,首先可以排除掉那些权值小于等于 $0$ 的字符串(出题人是认真的吗?)
构建出来所有单词的 $fail$ 树后,依次枚举每一个字符串,记该字符串在 $trie$ 树中终止节点为 $end(i)$.
那么,如果该单词包含了之前的一个单词,那么后缀就可能来自 $trie$ 树中根节点到该点.
依次枚举这条路径上的点,查询这条路径上在节点在 $fail$ 树对应的 $dfs$ 序上查询一下最大值.
而所有这些值的极大值就是 $i$ 为最后一个串的答案.
考虑 $i$ 可以对后面哪些串有贡献:就是 $fail$ 树中 $i$ 子树内的所有点,这个用线段树维护 $dfs$ 序即可.

#include <cstdio>
#include <queue>
#include <map>
#include <algorithm>
#include <cstring>
#define N 300002
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
int T,cas;
struct Seg {
#define lson (now<<1|1)
#define rson (now<<1)
struct Node {
int tag;
}t[N<<2];
void update(int l,int r,int now,int L,int R,int v) {
if(l>=L&&r<=R) {
t[now].tag=max(t[now].tag, v);
return;
}
int mid=(l+r)>>1;
if(L<=mid) update(l,mid,lson,L,R,v);
if(R>mid) update(mid+1,r,rson,L,R,v);
}
int query(int l,int r,int now,int p,int pre) {
pre=max(pre, t[now].tag);
if(l==r) return pre;
int mid=(l+r)>>1;
if(p<=mid) return query(l,mid,lson,p,pre);
else return query(mid+1,r,rson,p,pre);
}
#undef lson
#undef rson
}seg;
struct Node {
int f, ch[27];
}t[N];
queue<int>q;
char str[N];
int n,tot,tim,edges,w[N],endpos[N],hd[N],nex[N],to[N],dfn[N],size[N];
void addedge(int u,int v) {
nex[++edges]=hd[u],hd[u]=edges,to[edges]=v;
}
void dfs(int u) {
dfn[u]=++tim,size[u]=1;
for(int i=hd[u];i;i=nex[i]) dfs(to[i]), size[u]+=size[to[i]];
}
int insert() {
int len=strlen(str+1),i,rt=0;
for(i=1;i<=len;++i) {
if(!t[rt].ch[str[i]-'a']) t[rt].ch[str[i]-'a']=++tot;
rt=t[rt].ch[str[i]-'a'];
}
return rt;
}
void build() {
int i,j;
for(i=0;i<27;++i) if(t[0].ch[i]) q.push(t[0].ch[i]);
while(!q.empty()) {
int u=q.front();q.pop();
for(i=0;i<27;++i) {
int p=t[u].ch[i];
if(!p) {
t[u].ch[i]=t[t[u].f].ch[i];
continue;
}
t[p].f=t[t[u].f].ch[i];
q.push(p);
}
}
}
void solve() {
int i,j;
scanf("%d",&n);
for(i=1;i<=n;++i) {
scanf("%s%d",str+1,&w[i]);
if(w[i]>0) endpos[i]=insert();
}
build();
for(i=1;i<=tot;++i)
addedge(t[i].f,i);
dfs(0);
int answer=0;
for(i=1;i<=n;++i) {
if(w[i]<=0) continue;
int p=endpos[i],re=0;
while(p) re=max(re, seg.query(1,tim,1,dfn[p],0)), p=t[p].f;
re+=w[i];
answer=max(answer, re);
seg.update(1,tim,1,dfn[endpos[i]],dfn[endpos[i]]+size[endpos[i]]-1,re);
}
printf("%d\n",answer);
memset(hd,0,sizeof(hd)), memset(endpos,0,sizeof(endpos)), memset(t,0,sizeof(t));
tot=tim=edges=0;
memset(seg.t,0,sizeof(seg.t));
}
int main() {
// setIO("input");
scanf("%d",&T);
for(cas=1;cas<=T;++cas) solve();
return 0;
}

  

BZOJ 2905: 背单词 AC自动机+fail树+dfs序+线段树的更多相关文章

  1. BZOJ2434 [Noi2011]阿狸的打字机(AC自动机 + fail树 + DFS序 + 线段树)

    题目这么说的: 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的: 输入小 ...

  2. BZOJ4551[Tjoi2016&Heoi2016]树——dfs序+线段树/树链剖分+线段树

    题目描述 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均 ...

  3. BZOJ 3172: [Tjoi2013]单词 [AC自动机 Fail树]

    3172: [Tjoi2013]单词 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 3198  Solved: 1532[Submit][Status ...

  4. BZOJ2905: 背单词 AC自动机+fail树+线段树

    $zjq$神犇一眼看出$AC$自动机 $Orz$ 直接就讲做法了 首先对每个串建出$AC$自动机 将$fail$树找到 然后求出$dfs$序 我们发现一个单词 $S_i$是$S_j$的子串当且仅当$S ...

  5. 【BZOJ2905】背单词 fail树+DFS序+线段树

    [BZOJ2905]背单词 Description 给定一张包含N个单词的表,每个单词有个价值W.要求从中选出一个子序列使得其中的每个单词是后一个单词的子串,最大化子序列中W的和. Input 第一行 ...

  6. AC自动机fail树上dfs序建线段树+动态memset清空

    题意:http://acm.hdu.edu.cn/showproblem.php?pid=4117 思路:https://blog.csdn.net/u013306830/article/detail ...

  7. bzoj 1782: [Usaco2010 Feb]slowdown 慢慢游【dfs序+线段树】

    考虑每头牛到达之后的影响,u到达之后,从1到其子树内的点需要放慢的都多了一个,p为u子树内点的牛ans会加1 用线段树维护dfs序,每次修改子树区间,答案直接单点查询p即可 #include<i ...

  8. BZOJ 3172 [Tjoi2013]单词 AC自动机Fail树

    题目链接:[http://www.lydsy.com/JudgeOnline/problem.php?id=3172] 题意:给出一个文章的所有单词,然后找出每个单词在文章中出现的次数,单词用标点符号 ...

  9. DFS序+线段树(bzoj 4034)

    题目链接 题目就不多说了. 本题目,可以用dfs序+线段树做:题目给定了一棵树,树上节点告诉了权值.我们可以先将这棵树进行dfs将一棵树变成线性结构:如图 变成这样后,然后就可以用线段树. 操作1:也 ...

随机推荐

  1. 多标签分类(multi-label classification)综述

    意义 网络新闻往往含有丰富的语义,一篇文章既可以属于“经济”也可以属于“文化”.给网络新闻打多标签可以更好地反应文章的真实意义,方便日后的分类和使用. 难点 (1)类标数量不确定,有些样本可能只有一个 ...

  2. Spring Boot 五种热部署方式,极速开发就是生产力!

    1.模板热部署 在 Spring Boot 中,模板引擎的页面默认是开启缓存的,如果修改了页面的内容,则刷新页面是得不到修改后的页面的,因此我们可以在application.properties中关闭 ...

  3. AcWing175电路维修

    这是一道在luogu的蓝题,在yxc大佬的讲解下AC掉了(百般调试) 首先这道题给了一个字符串矩阵,/ \表示相连哪两个节点,只可以走/ \所连接的两个点,但我们可以旋转每一个边,询问从1,1 走到 ...

  4. 从入门到自闭之Python入门

    python是一门解释型编程语言 变量名命名的规则: 变量名由字母,数字,下划线组成 变量名不能以数字开头 变量名要具有可描述性 变量名要区分大小写 变量名禁止使用python关键字 变量名不能使用中 ...

  5. js数据结构-链表

    链表和数组 大家都用过js中的数组,数组其实是一种线性表的顺序存储结构,它的特点是用一组地址连续的存储单元依次存储数据元素.而它的缺点也正是其特点而造成,比如对数组做删除或者插入的时候,可能需要移动大 ...

  6. Codeforces 1178B. WOW Factor

    传送门 显然对每个 $o$ ,考虑左边和右边分别有多少 $w$,那么这个 $o$ 的贡献就是左右 $w$ 的出现次数相乘 $w$ 的出现次数可以直接根据每一段连续的 $v$ 得到 那么从左到右扫一遍, ...

  7. Git复习(十二)之命令专场

    命令 git init -> 初始化一个git仓库 git clone -> 克隆一个本地库 git pull -> 拉取服务器最新代码 git fetch –p -> 强行拉 ...

  8. 一、redis学习(基础)

    redis  持久化 rdb aof 

  9. python中字符串格式化的意义(化妆)

    格式 描述%% 百分号标记 #就是输出一个%%c 字符及其ASCII码%s 字符串%d 有符号整数(十进制)%u 无符号整数(十进制)%o 无符号整数(八进制)%x 无符号整数(十六进制)%X 无符号 ...

  10. 第三篇 HTML 表单及表格

    表单及表格 表单,常用在登录.注册等地方,这也是一个最基本的.   我们就用登录,来学习什么是表单!   表单 form 标签,在某些好用的编辑工具,比如:WebStorm  你在上面写出form再按 ...