题目链接: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求后缀树的更多相关文章

  1. 【bzoj4199】[Noi2015]品酒大会 后缀自动机求后缀树+树形dp

    题目描述(转自百度文库) 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒 ...

  2. [BZOJ4199][Noi2015]品酒大会 树形DP+后缀自动机

    由于要找后缀的前缀,所以先用反串建立SAM. link边组成了后缀树. 两个子串的最长公共前缀是LCA的step 树形dp即可. #include<iostream> #include&l ...

  3. UOJ131 [NOI2015] 品酒大会

    考前挣扎(bu shi 之前留下来的坑 首先注意到 SAM的parent树 是反串的后缀树 也就是原串的前缀树 这个性质很重要 所以说我们在树上统计的时候两个点的lca就是两个后缀串的lcp 于是可以 ...

  4. bzoj4199:NOI2015D2T2品酒大会(SAM版)

    SAM感觉写起来比SA更直观(?) #include <iostream> #include <cstdio> #include <cstring> #includ ...

  5. 【BZOJ4199&UOJ131】品酒大会(后缀数组,并查集)

    题意: 两杯“r相似” (r>1)的酒同时也是“1 相似”.“2 相似”.…….“(r−1) 相似”的. n<=300000 abs(a[i])<=10^9 思路:对于i,j两个后缀 ...

  6. BZOJ4199/UOJ131 [Noi2015]品酒大会

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

  7. bzoj4199: [Noi2015]品酒大会(后缀数组)

    题目描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战 两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainb ...

  8. BZOJ3879 SvT(后缀树+虚树)

    对反串建SAM得到后缀树,两后缀的lcp就是其在后缀树上lca的len值,于是每次询问对后缀树建出虚树并统计答案即可. #include<iostream> #include<cst ...

  9. Luogu5284 十二省联考2019字符串问题(后缀树+拓扑排序)

    对反串建SAM弄出后缀树,每个b串通过倍增定位其在后缀树上对应的节点,根据其长度将节点拆开.然后每个a串也找到对应的节点,由该节点向表示a串的节点连边,再把所给的边连上跑拓扑排序即可. #includ ...

随机推荐

  1. 事务消息中心-TMC

    此文已由作者杨凯明授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 背景 为什么要做事务消息中心 原有kqueue的方式缺点: 降低业务库性能 占用业务库磁盘 历史数据管理成本 ...

  2. PostgreSQL字段名和表名大小写的问题

    创建表的时候,表名和字段名必须全小写,然后查询的时候不管全大写或全小写,或是Camel模式都不会报错.只要名称中有大写字母,或者全大写,查询时就必须保证大小写正确并用双引号包起来,否则就会报“XXX不 ...

  3. C++知识点 内存占用问题

    有一次去面试,谈的挺好的,被人问了一个问题,瞬间暴露自己基础能力弱的弱点了,这里自己记录下,以后慢慢长进. 问题 char test1[]={1,2,3,4}; char test2[]={1,2,3 ...

  4. Selenium LoadableComponent加载组件

    继承LoadableComponent类可以在打开地址时, 判断浏览器是否打开了预期的网址, 需要重写load()与isLoad()方法: 即使没有定义get()方法, 也可以进行get()方法的调用 ...

  5. 软件测试面试题-适合零基础和工作多年的re

    软件测试面试题整理,可以看看:适合零基础和多年工作经验跳槽的人 有些问题会深挖,就不在整理了 详看图片:

  6. 二分图的最大匹配——Hopcroft-Karp算法

    http://blog.csdn.net/wall_f/article/details/8248373

  7. 软工实践Beta冲刺(1/7)

    队名:起床一起肝活队 组长博客:博客链接 作业博客:班级博客本次作业的链接 组员情况 组员1(队长):白晨曦 过去两天完成了哪些任务 描述: 1.界面的修改与完善 展示GitHub当日代码/文档签入记 ...

  8. Object类中的五种方法

    clone() Object类源码:protected native Object clone() throws CloneNotSupportedException; 这里有个问题:为什么Sun公司 ...

  9. Vue2.0 新手完全填坑攻略——从环境搭建到发布(转载)

    强力推荐,详情请点击此链接http://www.jianshu.com/p/5ba253651c3b

  10. elasticsearch this is not a http port

    访问的是elastic search的tcp端口,需换成http端口. elastic search默认tcp端口9300,http端口9200 如果浏览器中访问http://localhost:92 ...