G. 背单词

内存限制:256 MiB 时间限制:1000 ms 标准输入输出
题目类型:传统 评测方式:文本比较
 

题目描述

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

输入格式

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

输出格式

TEST行,每行一个整数,表示W的和的最大值。

数据规模 设字符串的总长度为Len 30.的数据满足,TEST≤5,N≤500,Len≤10^4 100.的数据满足,TEST≤10,N≤20000,Len≤3*10^5


析:又是一道好题,这道题将AC自动机与线段树,dp,以及 dfs 序结合了起来;

  首先我们要明确这样一个事情,S是T的字串,相当于 T  的一个前缀可以通过 Fail 树遍历到 S 的末尾结点,也就是说, S 的末尾节点是 T 的某个前缀在 Fail 树上的祖先;

  那么这道题思路就清晰了,首先可以写出 dp 方程 :f[i]=max(x)+w[i] ,表示 在前 i 个单词中,当前枚举到第 i 个单词且选择它的最大值, max(x) 表示当前单词前缀的最大值;

  那么此时我们的问题就在于 1.如何求得前缀? 2.如何求得区间(单点)最大值?

  对于第一个问题,我们可以使用 fa 数组进行回溯查询:

 if(!use[now].son[p])
{
use[now].son[p]=++num;
fa[use[now].son[p]]=now;
}

  这是在构建字典树的过程中记录每一个字符的父亲节点,我们在计算过程中就可以:

 while(p)
{
f[i]=max(f[i],query(rt,1,num,l[p]));
p=fa[p];
}

  那么考虑第二个问题,区间查询,我们同时还要考虑到,每次在我们枚举一次选择的单词后,我们都要判断选或不选哪个是最优解,然后对一段区间进行更新,所以说,我们不仅需要区间查询,还需要区间修改的操作,

   看这数据范围,显然我们可以想到线段树。那么我们在查询,修改的过程中如何确定区间呢? 这里利用 dfs 序就是一种很妙的思路,我们求出 in[],与out[] 就可以知道当前单词的控制区间。

  那么问题来了,我们要构建一颗什么样的 dfs 树呢?

  显然,若考虑当前单词 i ,fail[i] ,fa[i],fail[fa[i]] ,那么很明显,fail[fa[i]] 应该是控制区间最大的那一个,所以,我们就要从每个节点的 fail 指针向当前单词 连一条边,进行 dfs ;

  这里我们再解释一下为什么是单点查询,注意题目要求:

>  从中选出一个子序列使得其中的每个单词是后一个单词的子串

  1.假设现在有个序列 ABCD ,那么假设我的单词分别为 AB , 和 CD,那么如果我两个同时拿的话就无法满足题义

  2.若每次我们都之考虑某一个前缀的最大值,那么递推过来的一定是满足条件的最大值!!

  那么,在这颗线段树中,我们要维护的就是每个单词选或不选的最大值,所以在区间更新的时候我们都要取 max,到这里应该解释的差不多了;

 代码:

#include<bits/stdc++.h>
#define re register int
using namespace std;
const int N=3e6+10;
int T,n,cnt,tot,rt,timi,num=1;
char s[N];
int w[N],ed[N],l[N],r[N],fa[N];
int head[N],to[N<<1],next[N<<1];
long long f[N];
long long maxx;
bool vis[N];
queue<int> q;
struct CUN
{
int flag,fail;
int son[30];
void clean()
{
flag=0;
fail=0;
memset(son,0,sizeof(son));
}
}use[N];
struct C2
{
int lc,rc,sum,lazy;
void clean()
{
lc=0;
rc=0;
sum=0;
lazy=0;
}
}t[N];
void in()
{
for(re i=0;i<=max(cnt,tot);i++)
use[i].clean();
for(re i=0;i<=max(cnt,tot);i++)
t[i].clean();
cnt=0;
num=1;
maxx=0;
tot=0;
timi=0;
memset(l,0,sizeof(l));
memset(r,0,sizeof(r));
memset(vis,0,sizeof(vis));
memset(f,0,sizeof(f));
memset(w,0,sizeof(w));
memset(ed,0,sizeof(ed));
memset(head,0,sizeof(head));
memset(to,0,sizeof(to));
memset(next,0,sizeof(next));
memset(fa,0,sizeof(fa));
while(!q.empty())
q.pop();
}
void insert(char ss[],int pos)
{
int now=1;
int l=strlen(ss);
for(re i=0;i<l;i++)
{
int p=ss[i]-'a';
if(!use[now].son[p])
{
use[now].son[p]=++num;
fa[use[now].son[p]]=now;
}
now=use[now].son[p];
}
ed[pos]=now;
}
void Add(int x,int y)
{
to[++tot]=y;
next[tot]=head[x];
head[x]=tot;
}
void dfs(int x)
{
if(x)
l[x]=++timi;
for(re i=head[x];i;i=next[i])
dfs(to[i]);
r[x]=timi;
}
void build(int &p,int L,int R)
{
p=++cnt;
if(L==R)
return;
int mid=(L+R)>>1;
build(t[p].lc,L,mid);
build(t[p].rc,mid+1,R);
}
void get_fail()
{
for(re i=0;i<26;i++)
use[0].son[i]=1;
use[1].fail=0;
q.push(1);
while(!q.empty())
{
int u=q.front();
q.pop();
int Fail=use[u].fail;
for(re i=0;i<26;i++)
{
int v=use[u].son[i];
if(!v)
{
use[u].son[i]=use[Fail].son[i];
continue;
}
use[v].fail=use[Fail].son[i];
q.push(v);
}
}
for(re i=1;i<=num;i++)
Add(use[i].fail,i);
dfs(0);
build(rt,1,num);
}
void pd(int p)
{
if(t[p].lazy==0)
return;
t[t[p].lc].lazy=max(t[t[p].lc].lazy,t[p].lazy);
t[t[p].rc].lazy=max(t[t[p].rc].lazy,t[p].lazy);
t[t[p].lc].sum=max(t[t[p].lc].sum,t[p].lazy);
t[t[p].rc].sum=max(t[t[p].rc].sum,t[p].lazy);
t[p].lazy=0;
}
void pp(int rt)
{
t[rt].sum=max(t[t[rt].lc].sum,t[t[rt].rc].sum);
}
long long query(int rt,int L,int R,int p)
{
if(L==R)
return t[rt].sum;
int mid=(L+R)>>1;
pd(rt);
if(p<=mid)
return query(t[rt].lc,L,mid,p);
return query(t[rt].rc,mid+1,R,p);
}
void updata(int p,int L,int R,int l,int r,int z)
{
if(l<=L&&R<=r)
{
t[p].sum=max(t[p].sum,z);
t[p].lazy=max(t[p].lazy,z);
return;
}
pd(p);
int mid=(L+R)>>1;
if(mid>=l)
updata(t[p].lc,L,mid,l,r,z);
if(mid<r)
updata(t[p].rc,mid+1,R,l,r,z);
pp(p);
}
void dp()
{
//f[i]=max{x}+w[i];
for(re i=1;i<=n;i++)
{
int p=ed[i];
while(p)
{
f[i]=max(f[i],query(rt,1,num,l[p]));
p=fa[p];
}
f[i]=max(0*1ll,f[i]+w[i]);
updata(rt,1,num,l[ed[i]],r[ed[i]],f[i]);
}
for(re i=1;i<=n;i++)
maxx=max(maxx,f[i]);
printf("%lld\n",maxx);
}
signed main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
in();
for(re i=1;i<=n;i++)
{
scanf("%s",s);
scanf("%d",&w[i]);
insert(s,i);
}
get_fail();
dp();
}
}

背单词(AC自动机+线段树+dp+dfs序)的更多相关文章

  1. BZOJ 2905: 背单词 AC自动机+fail树+dfs序+线段树

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

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

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

  3. hdu 4117 GRE Words (ac自动机 线段树 dp)

    参考:http://blog.csdn.net/no__stop/article/details/12287843 此题利用了ac自动机fail树的性质,fail指针建立为树,表示父节点是孩子节点的后 ...

  4. hdu 4117 -- GRE Words (AC自动机+线段树)

    题目链接 problem Recently George is preparing for the Graduate Record Examinations (GRE for short). Obvi ...

  5. CF877E Danil and a Part-time Job 线段树维护dfs序

    \(\color{#0066ff}{题目描述}\) 有一棵 n 个点的树,根结点为 1 号点,每个点的权值都是 1 或 0 共有 m 次操作,操作分为两种 get 询问一个点 x 的子树里有多少个 1 ...

  6. HDU4117 GRE WORDS(AC自动机+线段树维护fail树的dfs序)

    Recently George is preparing for the Graduate Record Examinations (GRE for short). Obviously the mos ...

  7. BZOJ2434:[NOI2011]阿狸的打字机(AC自动机,线段树)

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

  8. HDU 5069 Harry And Biological Teacher(AC自动机+线段树)

    题意 给定 \(n\) 个字符串,\(m\) 个询问,每次询问 \(a\) 字符串的后缀和 \(b\) 字符串的前缀最多能匹配多长. \(1\leq n,m \leq 10^5\) 思路 多串匹配,考 ...

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

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

随机推荐

  1. 安卓控件RecycleView的简单使用

    RecycleView的使用 目录 RecycleView的使用 技术概述 技术详述 遇到问题和解决 总结 参考文献 技术概述 RecycleView是谷歌官方对ListView的改进(并不是替代), ...

  2. RESTful API实践总结

    REST架构 你是如何理解上网这件事的? 打开浏览器,输入网址,展现在你面前的就是一个网站了. 你可以在网站里看视频.看博客.写文章.听音乐. 但凡写过点代码的人都知道,我们平时访问的网站,其实是HT ...

  3. CAS你知道吗?底层如何实现?ABA问题又是什么?关于这些你知道答案吗

    CAS你知道吗?如何实现? 1. compareAndSet 在volatile当中我们提到,volatile不能保证原子语义,所以当用到变量自增时,如果用到synchronized会太"重 ...

  4. zbxtable的使用

    实验环境: zabbix server 172.16.1.121 mysql 172.16.1.121 访问端 172.16.1.122 54.1 zbxtable 1 说明 ZbxTable使用Go ...

  5. 15.3、mysql之InnoDB和MyISAM表空间详解

    15.3.1.InnoDB引擎表空间: 1.表空间分类: 共享表空间: 某一个数据库的所有的表数据,索引文件全部放在一个文件中,默认这个共享表空间的文件路径在 data目录下. 默认的文件名为:ibd ...

  6. AcWing 1293. 夏洛克和他的女朋友

    夏洛克有了一个新女友(这太不像他了!). 情人节到了,他想送给女友一些珠宝当做礼物. 他买了n件珠宝,第i件的价值是i+1. 华生挑战夏洛克,让他给这些珠宝染色,使得一件珠宝的价格是另一件珠宝的价格的 ...

  7. Linux安装mysql5.7版本

    1.linux安装mysql5.7顺序 ①mysqladmin –version 查看版本号 ②mysql5.7安装在linux中需要先初始化 Mysqld –initialize –user=mys ...

  8. 解决MyEclipse一直在Updating indexes的文题

    Updating indexes是Maven在下载更新,这个需要手动去设置即可:  Window --> Preferences --> Maven4MyEclipse --> 去除 ...

  9. php解决约瑟夫环

    今天偶遇一道算法题 "约瑟夫环"是一个数学的应用问题:一群猴子排成一圈,按1,2,-,n依次编号.然后从第1只开始数,数到第m只,把它踢出圈,从它后面再开始数, 再数到第m只,在把 ...

  10. XCTF re-100

    一.无壳并拉入ida64静态调试(注释说的很明白了) 二.confuseKey是个关键函数,进入看看 发现就是将我们所输入的字符串分割,并把顺序调换了,调回来就是我们的flag. 三.flag: 提交 ...