【UOJ131/NOI2015D2T2-品酒大会】sam求后缀树
题目链接:http://uoj.ac/problem/131
题意:给出一个字符串,第i个字符对应的值为a[i], 对于i∈[0,n),求最长公共前缀大于等于i的字串对个数,并求这些字符串对开头对应值相乘最大值。n=3*10^5
题解:
学了个厉害的东西啊。。。
正解好像是sa+并查集(合并height)
然而我学了个用sam的做法。。
对于第一问:
首先我们要知道,建立后缀自动机之后,parent树就是逆序串的后缀树。
why?看这个博客好了:http://z55250825.blog.163.com/blog/static/15023080920144542541495/
直接逆序建后缀自动机,
因为对于现在parent树而言,任意两点的LCP等于两点在树上的LCA的step(step就是sam里的那个step。。一开始没想清楚还以为是parent-tree上的深度。。于是WA了。。)
这是转化成一个简单的树形dp了:按逆拓扑序更新(从孩子到parent),对于当前点x,看它是多少对点对的lcp。
假设有四个孩子,孩子的点数(就是这棵子树上有多少个点)分别为s1,s2,s3,s4
cnt[x]=1*(s1+s2+s3+s4)(这是x到x的孩子) + (s1+s2+s3)*s4 + (s1+s2)*s3 + s1*s2
那我们每遍历一个孩子y,就sum[x]+=sum[y],对于一个新的孩子yy,cnt[x]+=sum[x]*sum[yy];
对于第二问:
对于当前的parent树而言,等价于求parent树上两个叶节点乘积的最大值。
又因为考虑到ai可能是负数,所以我们只需要记录最大值,次大值,最小值,次小值就可以了。
参考题解:http://www.cnblogs.com/joyouth/p/5366396.html
注意很多细节。。
sam真的超厉害。。可以直接转化成后缀树和后缀数组。。
ORZ。。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std; typedef long long LL;
const int N=**;
const LL INF=1LL<<;
int sl,cl,tot,last,c[N],in[N],first[N],step[N],pre[N],son[N][];
LL w[N],cnt[N],ans[N],mx[N],smx[N],mn[N],smn[N],sum[N];
char s[N];
bool vis[N];
queue<int> Q; LL maxx(LL x,LL y){return x>y ? x:y;}
LL minn(LL x,LL y){return x<y ? x:y;}
void gmax(LL &x,LL y){x=maxx(x,y);}
void gmin(LL &x,LL y){x=minn(x,y);} int add_node(int x)
{
step[++tot]=x;
return tot;
} void clear()
{
memset(son,,sizeof(son));
memset(pre,,sizeof(pre));
memset(step,,sizeof(step));
memset(in,,sizeof(in));
// memset(cnt,0,sizeof(cnt));
memset(sum,,sizeof(sum));
tot=;add_node();last=;
} void extend(int ch)
{
int p=last,np=add_node(step[p]+);
while(p && !son[p][ch])
{
son[p][ch]=np;
in[np]++;
p=pre[p];
}
if(!p) pre[np]=;
else
{
int q=son[p][ch];
if(step[q]==step[p]+) pre[np]=q;
else
{
int nq=add_node(step[p]+);
for(int i=;i<=;i++)
if(son[q][i]) son[nq][i]=son[q][i],in[son[q][i]]++;
pre[nq]=pre[q];
pre[np]=pre[q]=nq;
while(p && son[p][ch]==q) in[q]--,in[nq]++,son[p][ch]=nq,p=pre[p];
}
}
last=np;
} void get_tp()
{
while(!Q.empty()) Q.pop();
memset(vis,,sizeof(vis));
Q.push();vis[]=;cl=;
while(!Q.empty())
{
int x=Q.front();c[++cl]=x;vis[x]=;Q.pop();
for(int i=;i<=;i++)
{
int y=son[x][i];
if(!y) continue;
in[y]--;
if(!in[y] && !vis[y]) vis[y]=,Q.push(y);
}
}
} int main()
{
freopen("a.in","r",stdin);
int x,y,ch;
scanf("%d",&sl);
scanf("%s",s+);
for(int i=;i<=sl;i++) scanf("%lld",&w[i]); clear();
for(int i=sl;i>=;i--) extend(s[i]-'a'+);
get_tp(); for(int i=;i<=tot;i++) mx[i]=-INF,smx[i]=-INF,mn[i]=INF,smn[i]=INF;
x=;
for(int i=sl;i>=;i--)
{
ch=s[i]-'a'+;
x=son[x][ch];
mx[x]=mn[x]=w[i];
sum[x]++;
} LL tmp;
memset(cnt,,sizeof(cnt));
for(int i=;i<=sl;i++) ans[i]=-INF;
for(int i=cl;i>=;i--)
{
y=c[i],x=pre[y];
tmp=-INF;
if(smx[y]>-INF) gmax(tmp,mx[y]*smx[y]);
if(smn[y]<INF) gmax(tmp,mn[y]*smn[y]);
gmax(ans[step[y]],tmp);
cnt[step[x]]+=sum[x]*sum[y];
sum[x]+=sum[y]; if(mx[y]>=mx[x]) smx[x]=mx[x],mx[x]=mx[y];//debug >=
else if(mx[y]>smx[x]) smx[x]=mx[y];
if(mn[y]<=mn[x]) smn[x]=mn[x],mn[x]=mn[y];//debug <=
else if(mn[y]<smn[x]) smn[x]=mn[y];
}
// for(int i=0;i<sl;i++) printf("x = %d cnt = %lld ans = %lld\n",i,cnt[i],ans[i]);
for(int i=sl-;i>=;i--)
{ cnt[i]+=cnt[i+];
gmax(ans[i],ans[i+]);
}
for(int i=;i<sl;i++)
{
if(!cnt[i]) ans[i]=;
printf("%lld %lld\n",cnt[i],ans[i]);
}
return ;
}
【UOJ131/NOI2015D2T2-品酒大会】sam求后缀树的更多相关文章
- 【bzoj4199】[Noi2015]品酒大会 后缀自动机求后缀树+树形dp
题目描述(转自百度文库) 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒 ...
- [BZOJ4199][Noi2015]品酒大会 树形DP+后缀自动机
由于要找后缀的前缀,所以先用反串建立SAM. link边组成了后缀树. 两个子串的最长公共前缀是LCA的step 树形dp即可. #include<iostream> #include&l ...
- UOJ131 [NOI2015] 品酒大会
考前挣扎(bu shi 之前留下来的坑 首先注意到 SAM的parent树 是反串的后缀树 也就是原串的前缀树 这个性质很重要 所以说我们在树上统计的时候两个点的lca就是两个后缀串的lcp 于是可以 ...
- bzoj4199:NOI2015D2T2品酒大会(SAM版)
SAM感觉写起来比SA更直观(?) #include <iostream> #include <cstdio> #include <cstring> #includ ...
- 【BZOJ4199&UOJ131】品酒大会(后缀数组,并查集)
题意: 两杯“r相似” (r>1)的酒同时也是“1 相似”.“2 相似”.…….“(r−1) 相似”的. n<=300000 abs(a[i])<=10^9 思路:对于i,j两个后缀 ...
- BZOJ4199/UOJ131 [Noi2015]品酒大会
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...
- bzoj4199: [Noi2015]品酒大会(后缀数组)
题目描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战 两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainb ...
- BZOJ3879 SvT(后缀树+虚树)
对反串建SAM得到后缀树,两后缀的lcp就是其在后缀树上lca的len值,于是每次询问对后缀树建出虚树并统计答案即可. #include<iostream> #include<cst ...
- Luogu5284 十二省联考2019字符串问题(后缀树+拓扑排序)
对反串建SAM弄出后缀树,每个b串通过倍增定位其在后缀树上对应的节点,根据其长度将节点拆开.然后每个a串也找到对应的节点,由该节点向表示a串的节点连边,再把所给的边连上跑拓扑排序即可. #includ ...
随机推荐
- Python操作nosql数据库之redis
一.NoSQL的操作 NoSQL,泛指非关系型的数据库.随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不 ...
- python接口测试(三)——Excell文件读取进行参数化
python进行http请求时,需要对参数进行参数化,此时就可以运用Excel进行,具体如下: 1.梳理出请求中那些参数需要参数化,然后新建一个Excel,如图: 2.读取Excel中的内容,在读取前 ...
- GFS文件系统
1.1 分布式文件系统 1.1.1 什么是分布式文件系统 相对于本机端的文件系统而言,分布式文件系统(英语:Distributed file system, DFS),或是网络文件系统(英语:Ne ...
- Python登录小程序
------------------------------------------------- 主要实现功能 1.用户输入用户名,在用户名文件中查找对应的用户,若无对应用户名则打印输入错误 2.用 ...
- 安装 Win10 & Ubuntu 16.04 双系统以及 Ubuntu 配置深度学习环境记录
0. 前言 坑爹的Ubuntu晚上运行还是好好的,第二天中午的时候打开机器发现屏幕分辨率不正常了:2K屏显示800*600左右的分辨率(无法调节),一个图标一拳头大,窗口和网页显示不全.Google查 ...
- CentOS 6.5 下安装redis
1.登录虚拟机后,直接输入命令:yum -y install redis 会出现一个错误: 是因为少了epel源, 2.运行:yum -y install epel-release 最后出现 Comp ...
- 使用HashOperations操作redis
方法 c参数 s说明 Long delete(H key, Object... hashKeys); H key:集合key Object... hashKeys:key对应hashkey 删除ma ...
- 【转】Virtual DOM
前言 React 好像已经火了很久很久,以致于我们对于 Virtual DOM 这个词都已经很熟悉了,网上也有非常多的介绍 React.Virtual DOM 的文章.但是直到前不久我专门花时间去学习 ...
- 支持ie的时间控件 html
连接:http://www.my97.net/demo/resource/2.4.asp#m248 下载测试:链接: https://pan.baidu.com/s/17AdRa2OTLPI7ndiA ...
- 算法(7)Majority Element II
题目:找出数组中出现次数大于n/3次的数字 思路:摩尔投票法.所有的帖子中都说:先遍历一遍数组找到备选元素,然后再遍历一遍数组考察下这个元素是否是真的超过n/3,然后就直接上代码,但是现在的问题是:我 ...