【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 ' ...
随机推荐
- Atitit.基于时间戳的农历日历历法日期计算
Atitit.基于时间戳的农历日历历法日期计算 1. 农历xx年的大小月份根据万年历查询1 2. 农历xx年1月1日的时间戳获取1 3. 计算当年的时间戳与农历日期的对应表,时间戳为key,日期为va ...
- Atitit. 解决80端口 System 占用pid 4,,找到拉个程序或者服务占用http 80服务
Atitit. 解决80端口 System 占用pid 4,,找到拉个程序或者服务占用http服务 这个是http.sys系统服务占用了... net stop http ,三,没法儿终止 1. 寻 ...
- CA证书的办理
北京市地方税务局与北京数字证书认证中心(Beijing Certificate Authority ,简称BJCA)合作, 在北京市地方税务局网上纳税及其他网上业务系统中使用BJCA数字证书, 北京数 ...
- Caliburn Micro框架快速上手(WP)
一.使用nuget添加起始工程 二.修改App.xaml文件和App.xaml.cs文件 AppBootstrapper介绍: AppBootstrapper根据中文的直译可以 ...
- 一款基于css3鼠标经过圆形旋转特效
今天给大家分享一款基于css3鼠标经过圆形旋转特效.当鼠标经过的时候图片边框颜色旋转,图片显示详情.该实例适用浏览器:IE8.360.FireFox.Chrome.Safari.Opera.傲游.搜狗 ...
- 最新phpcms v9.6.0 sql注入漏洞分析
昨天爆出来的,但其实在此之前就i记得在某群看见有大牛在群里装逼了.一直也没肯告诉.现在爆出来了.就来分析一下.官方现在也还没给出修复.该文不给出任何利用的EXP. 该文只做安全研究,不做任何恶意攻击! ...
- sql row_number 用法
自己研究了一下RowNum ,发现这样的分页挺清晰的 --第几页,一页多少行declare @PageIndex int,@PageMax intset @PageIndex =1set @PageM ...
- 你所不知道的JSON
译者按: 老司机们,你知道JSON.stringify还有第二个和第三个可选参数吗?它们是什么呢? JSON已经逐渐替代XML被全世界的开发者广泛使用.本文深入讲解JavaScript中使用JSON. ...
- 调用半截的div
不能引用jquery: <script src="${rootUrl }js/jquery/jquery.js" type="text/javascript&quo ...
- 标签响应javascript的href处理[转载]
为了给一个<a />标签绑定javascript,但又不让它跳转链接,大家习惯上用的都是 <a href="javascript:;" onclick=" ...