【BZOJ2905】背单词 fail树+DFS序+线段树
【BZOJ2905】背单词
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自动机,然后A串是B串的子串当且仅当B中某个节点沿着fail树往根走,能走到A的结束节点。那么我们先将权值<=0的串都扔掉,然后从前往后枚举每个字符串,对于每个串,我们查询它的每个节点到根路径上的所有节点的DP值的最大值,然后用最大值+当前串的价值得到当前点的DP值,最后将当前串的DP值存到当前串的结束节点位置上。
查询最大值的时候可以将链查询,点修改变成点查询,子树修改,然后用线段树维护即可。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>
#define lson x<<1
#define rson x<<1|1
using namespace std;
const int maxn=20010;
const int maxm=300010;
typedef long long ll;
struct node
{
int ch[26],fail;
}p[maxm];
queue<int> q;
int T,n,tot,cnt;
ll ans,sum;
int v[maxn],to[maxm],next[maxm],head[maxm],p1[maxm],p2[maxm];
char str[maxm];
vector<int> pos[maxn];
ll s[maxm<<2],tag[maxm<<2];
inline void build()
{
register int i,u;
q.push(1);
while(!q.empty())
{
u=q.front(),q.pop();
for(i=0;i<26;i++)
{
if(!p[u].ch[i])
{
if(u==1) p[u].ch[i]=1;
else p[u].ch[i]=p[p[u].fail].ch[i];
continue;
}
q.push(p[u].ch[i]);
if(u==1) p[p[u].ch[i]].fail=1;
else p[p[u].ch[i]].fail=p[p[u].fail].ch[i];
}
}
}
inline void add(int a,int b)
{
to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
}
void dfs(int x)
{
p1[x]=++p2[0];
for(int i=head[x];i!=-1;i=next[i]) dfs(to[i]);
p2[x]=p2[0];
}
inline void pushdown(int x)
{
if(tag[x])
{
s[lson]=max(s[lson],tag[x]),s[rson]=max(s[rson],tag[x]);
tag[lson]=max(tag[lson],tag[x]),tag[rson]=max(tag[rson],tag[x]);
tag[x]=0;
}
}
void updata(int l,int r,int x,int a,int b,ll c)
{
if(a<=l&&r<=b)
{
s[x]=max(s[x],c),tag[x]=max(tag[x],c);
return ;
}
pushdown(x);
int mid=(l+r)>>1;
if(a<=mid) updata(l,mid,lson,a,b,c);
if(b>mid) updata(mid+1,r,rson,a,b,c);
s[x]=max(s[lson],s[rson]);
}
ll query(int l,int r,int x,int a)
{
if(l==r) return s[x];
pushdown(x);
int mid=(l+r)>>1;
if(a<=mid) return query(l,mid,lson,a);
return query(mid+1,r,rson,a);
}
inline void work()
{
register int i,j,a,b,u;
memset(s,0,sizeof(s[0])*4*(tot+1)),memset(tag,0,sizeof(tag[0])*4*(tot+1)),memset(p,0,sizeof(p[0])*(tot+1));
scanf("%d",&n);
tot=1,cnt=p2[0]=0,ans=0;
for(i=1;i<=n;i++)
{
scanf("%s%d",str,&v[i]),a=strlen(str);
if(v[i]<=0) continue;
pos[i].clear();
for(u=1,j=0;j<a;j++)
{
b=str[j]-'a';
if(!p[u].ch[b]) p[u].ch[b]=++tot;
u=p[u].ch[b],pos[i].push_back(u);
}
}
build();
memset(head,-1,sizeof(head[0])*(tot+1));
for(i=2;i<=tot;i++) add(p[i].fail,i);
dfs(1);
for(i=1;i<=n;i++) if(v[i]>0)
{
for(a=pos[i].size(),sum=0,j=0;j<a;j++) sum=max(sum,query(1,tot,1,p1[pos[i][j]]));
sum+=v[i],ans=max(ans,sum);
updata(1,tot,1,p1[pos[i][a-1]],p2[pos[i][a-1]],sum);
}
printf("%lld\n",ans);
}
int main()
{
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
scanf("%d",&T);
while(T--) work();
return 0;
}//1 5 a 1 ab 1 ac 4 abc 2 aa 1
【BZOJ2905】背单词 fail树+DFS序+线段树的更多相关文章
- BZOJ2434 [Noi2011]阿狸的打字机(AC自动机 + fail树 + DFS序 + 线段树)
题目这么说的: 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的: 输入小 ...
- BZOJ4551[Tjoi2016&Heoi2016]树——dfs序+线段树/树链剖分+线段树
题目描述 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均 ...
- BZOJ 2905: 背单词 AC自动机+fail树+dfs序+线段树
Description 给定一张包含N个单词的表,每个单词有个价值W.要求从中选出一个子序列使得其中的每个单词是后一个单词的子串,最大化子序列中W的和. Input 第一行一个整数TEST,表示数据组 ...
- Educational Codeforces Round 6 E dfs序+线段树
题意:给出一颗有根树的构造和一开始每个点的颜色 有两种操作 1 : 给定点的子树群体涂色 2 : 求给定点的子树中有多少种颜色 比较容易想到dfs序+线段树去做 dfs序是很久以前看的bilibili ...
- 【BZOJ-3252】攻略 DFS序 + 线段树 + 贪心
3252: 攻略 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 339 Solved: 130[Submit][Status][Discuss] D ...
- Codeforces 343D Water Tree(DFS序 + 线段树)
题目大概说给一棵树,进行以下3个操作:把某结点为根的子树中各个结点值设为1.把某结点以及其各个祖先值设为0.询问某结点的值. 对于第一个操作就是经典的DFS序+线段树了.而对于第二个操作,考虑再维护一 ...
- POJ 3321 DFS序+线段树
单点修改树中某个节点,查询子树的性质.DFS序 子树序列一定在父节点的DFS序列之内,所以可以用线段树维护. 1: /* 2: DFS序 +线段树 3: */ 4: 5: #include < ...
- 【XSY2667】摧毁图状树 贪心 堆 DFS序 线段树
题目大意 给你一棵有根树,有\(n\)个点.还有一个参数\(k\).你每次要删除一条长度为\(k\)(\(k\)个点)的祖先-后代链,问你最少几次删完.现在有\(q\)个询问,每次给你一个\(k\), ...
- F - Change FZU - 2277 (DFS序+线段树)
题目链接: F - Change FZU - 2277 题目大意: 题意: 给定一棵根为1, n个结点的树. 有q个操作,有两种不同的操作 (1) 1 v k x : a[v] += x, a[v ' ...
随机推荐
- 连接oracle时报错:ORA-28001: the password has expired
调试Web项目的时候出现异常: java.sql.SQLException: ORA-28001: the password has expired 网上查了一下,是Oracle11g密码过期的原因 ...
- FTL页面常用到的一些方法combobox、combotree、datagrid
参考文件:点击下载 1.combobox: (1).js 1)初始化combobox //相似度 $('#same').combobox({ //url:"<@s.url value= ...
- redis五种基本数据类型
1.string类:一个key对应一个value(key:value).string类是二进制安全,可以包含任何数据(例如:图片.音乐). 2.hash类:string类型field和value的映射 ...
- Python中使用UUID
import uuid ... ... print uuid.uuid1() 生成的方法还有uuid2..n,具体参见官网LINK,包括参数细则
- 基于redis的分布式缓存disgear开源到github上了
disgear是笔者参考solrcloud架构基于redis实现的分布式的缓存,支持数据切分到多台机器上,支持HA,支持读写分离和主节点失效自动选举,目前把它开放到github上,开放给大家 gith ...
- TIME_WAIT详解
1.TCP四次挥手关闭链接过程 2.TIME_WAIT的产生条件主动关闭方在发送四次挥手的最后一个ACK会变为TIME_WAIT状态,保留此状态的时间为两个MSL 3.TIME_WAIT两个MSL的作 ...
- 算法图绘制工具Graphviz
graphviz是贝尔实验室设计的一个开源的画图工具,它的强大主要体现在“所思即所得"(WYTIWYG,what you think is what you get),这是和office的“ ...
- java ssm框架入门(二)添加语言滤器
使用过滤器是在web.xml中使用filter,以下是码过滤器,过滤所有资源的使用 web.xml <filter> <filter-name>setCharactor< ...
- boost准模板库内存管理中pool和object_pool的使用
首先,在敲代码之前,必须改动一个问题.要不然,无法链接: boost安装文件夹:D:\boost. 找到D:\boost\boost_1_55_0\include\boost-1_55\b ...
- 编写自己的代码库(javascript常用实例的实现与封装)
https://segmentfault.com/a/1190000010225928