【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 ...
随机推荐
- 「暑期训练」「Brute Force」 Multiplication Table (CFR256D2D)
题意 给定一矩阵M" role="presentation">MM,Mij=ij" role="presentation">Mi ...
- join ,left join ,right join有什么区别
join等价于inner join内连接,是返回两个表中都有的符合条件的行. left join左连接,是返回左表中所有的行及右表中符合条件的行.(左表为主表) right join右连接,是返回右表 ...
- C#异步了解一下
如何让你的代码在“同一时间”干着两件件事呢?比如说,在初始化加载配置的同时,UI界面能够响应用户的各种点击事件.而不置于卡死,特别是出现如下面这种情况的时候,对于用户来说是很崩溃的.
- Qt 个性化标题栏,自定义标题栏
目前还没有达到自己满意的地步,魔方别人写的的,先提供参考,后面在加入新的东西 头文件 #ifndef TITLEBAR_H #define TITLEBAR_H #include <QWidge ...
- 自动化测试---mybatis的使用
mybatis如何实现了对数据库的操作: 1.通过Resources.getResourceAsReader()或者 Resources.getResourceAsStream()加载mybatis. ...
- Docker容器-入门级
1.1 容器简介 1.1.1 什么是 Linux 容器 Linux容器是与系统其他部分隔离开的一系列进程,从另一个镜像运行,并由该镜像提供支持进程所需的全部文件.容器提供的镜像包含了应用的所有依赖项, ...
- LeetCode 3——无重复字符的最长子串
1. 题目 2. 解答 2.1. 方法一 我们从前往后遍历字符串,start 代表最长子串的起始位置,一开始设置为零. 如果没有遇到重复字符,则更新子串的长度,向后遍历. 如果遇到重复字符时,则更新字 ...
- c# 自动关机代码
#region 关机代码 //C#关机代码 // 这个结构体将会传递给API.使用StructLayout //(...特性,确保其中的成员是按顺序排列的,C#编译器不会对其进行调整. [Struct ...
- java long值转成时间格式
/** * 将long值转换为以小时计算的格式 * @param mss * @return */ public static String formatLongTime(long mss) { St ...
- multi-tap
multi-tap又称 multi-press . 是在手机,或者电视遥控上的keypad定义,有如下2类标准: 1. ITU-T E.161 2.T9 使用举例如下: Consider a typi ...