题目链接: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. Python操作nosql数据库之redis

    一.NoSQL的操作 NoSQL,泛指非关系型的数据库.随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不 ...

  2. 导入execl到数据库mysql

    GwykhrenyuankuList <body jwcid="$content$"> <span jwcid="@components/AppBord ...

  3. ArcGIS Server远程处理服务器(环境设置)

    当使用ArcGIS Server做远程处理服务器执行影像处理操作时,提示ERROR 999999通用错误代码,如下: Start Time: Mon Jul 03 13:49:06 2017Distr ...

  4. 使用PSSH批量操作Linux服务器

    简介 服务器多了,有一个问题就是如何批量快速操作多台服务器,在网上搜到了PSSH工具,试用了一下发现挺好用,推荐给大家. pssh是一个python编写的可以在多台服务器上执行命令的轻量级管理工具,同 ...

  5. 【功能笔记】Ubuntu查看系统资源占用(内存,cpu和进程) {转载}

    转载自http://bluexp29.blog.163.com/blog/static/33858148201071534450856/ linux真是太强大了. 查看ubuntu的资源占用的命令为$ ...

  6. 虚拟现实-VR-UE4-获取UE4

    UE4现在虽然是开源,但是并不是免费的,在你的游戏成功后,回收取5%费用和每个月19美元的费用 所以,第一步,进去UE4官网:https://www.unrealengine.com/zh-CN/wh ...

  7. C语言关于“输入包含多行数据,请处理到文件结束”的问题

    今天,笔者在做本校ACM校赛网络赛的时候,遇到输入格式中有这样的要求:输入包含多行数据,请处理到文件结束.题目的逻辑很简单,主要功能代码很容易实现,但是题目中没有“明确”指出控制台中输入数据以什么方式 ...

  8. SpringBoot:工厂模式实现定时任务可配置

    pringBoot:工厂模式实现定时任务可配置 需要:使用springboot,实现定时任务可配置. 定时任务可在代码中写死,在配置文件中配置,这些都不能实现定时任务在服务器不重启的情况下可配置. 为 ...

  9. Week7 Teamework from Z.XML-任务分配

    任务分配 Z.XML任务初步分配新鲜出炉,请关注! 初步估计,我们的项目需要191小时.但是根据敏捷开发的方法,我们将在开发过程中根据情况迅速调整任务分配,以适应当时问题.

  10. 微信小程序-腾讯地图显示偏差问题

    原文地址: http://fanjiajia.cn/2018/08/30/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F-%E8%85%BE%E8%AE%A ...