http://uoj.ac/problem/131 (题目链接)

题意

  给出一个字符串,每个后缀有一个权值${a_i}$,这些后缀两两之间存在公共前缀。问能够组成长度从0~n-1的公共前缀的后缀的方案数以及他们权值的最大乘积。

Solution

  听LCF说这是水题,就来做了。。

  lyp学长说SAM构出来后就两个东西:在自动机上dp,在后缀树上搞事情。

  于是我们很容易想到在后缀树上dp(感觉在自动机上dp没什么卵用),于是我们把串反过来见后缀自动机,然后建出后缀树,在上面树形dp就可以了。

  假设当前dfs到了节点x。我们用mx[x][0,1]记录当前子树中最大的两个后缀的权值,因为权值可能为负数,所以还要用mn[x][0,1]记录当前子树中最小的两个后缀的权值,sum[x]记录当前子树中后缀的个数。这样你发现这些信息就能够更新长度为0~len[x]的答案,其中len[x]表示其在后缀树中的“深度”(其实深度这个表示并不是特别妥当→_→),那么就可以做了。

细节

  细节多的抠脚,建议想清楚再写→_→不,是深夜写题果然要不得→_→

代码

// uoj131
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf 1ll<<60
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std; const int maxn=300010;
int n;
char s[maxn];
LL a[maxn],ans[maxn],cnts[maxn]; namespace SAM {
int len[maxn<<1],par[maxn<<1],pos[maxn<<1],ch[maxn<<1][26];
int last,Dargen,sz,cnt,head[maxn<<1];
LL mx[maxn<<1][2],mn[maxn<<1][2],sum[maxn<<1]; struct edge {int to,next;}e[maxn<<2]; void link(int u,int v) {
e[++cnt]=(edge){v,head[u]};head[u]=cnt;
}
void Extend(int c) {
int np=++sz,p=last;last=np;
len[np]=pos[np]=len[p]+1;
for (;p && !ch[p][c];p=par[p]) ch[p][c]=np;
if (!p) par[np]=Dargen;
else {
int q=ch[p][c];
if (len[p]+1==len[q]) par[np]=q;
else {
int nq=++sz;len[nq]=len[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
par[nq]=par[q];
par[np]=par[q]=nq;
for (;p && ch[p][c]==q;p=par[p]) ch[p][c]=nq;
}
}
}
void build() {
last=Dargen=sz=1;
for (int i=1;i<=n>>1;i++) swap(s[i],s[n-i+1]),swap(a[i],a[n-i+1]);
for (int i=1;i<=n;i++) Extend(s[i]-'a');
}
void dfs(int x) {
mx[x][0]=mx[x][1]=-inf;
mn[x][0]=mn[x][1]=inf;
if (pos[x]) mn[x][0]=mx[x][0]=a[pos[x]],sum[x]=1;
for (int i=head[x];i;i=e[i].next) {
dfs(e[i].to);
cnts[len[x]]+=sum[x]*sum[e[i].to];
sum[x]+=sum[e[i].to];
if (mx[x][0]<mx[e[i].to][0]) {
mx[x][1]=mx[x][0],mx[x][0]=mx[e[i].to][0];
}
else if (mx[x][1]<mx[e[i].to][0]) mx[x][1]=mx[e[i].to][0];
if (mx[x][1]<mx[e[i].to][1]) mx[x][1]=mx[e[i].to][1];
if (mn[x][0]>mn[e[i].to][0]) {
mn[x][1]=mn[x][0],mn[x][0]=mn[e[i].to][0];
}
else if (mn[x][1]>mn[e[i].to][0]) mn[x][1]=mn[e[i].to][0];
if (mn[x][1]>mn[e[i].to][1]) mn[x][1]=mn[e[i].to][1];
}
if (mx[x][0]!=-inf && mx[x][1]!=-inf) ans[len[x]]=max(ans[len[x]],mx[x][0]*mx[x][1]);
if (mn[x][0]!=inf && mn[x][1]!=inf) ans[len[x]]=max(ans[len[x]],mn[x][0]*mn[x][1]);
}
void prepare() {
for (int i=2;i<=sz;i++) link(par[i],i);
for (int i=0;i<=n;i++) ans[i]=-inf;
dfs(Dargen);
}
}
using namespace SAM; int main() {
scanf("%d",&n);
scanf("%s",s+1);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
build();
prepare();
for (int i=n-1;i>=0;i--) {
ans[i]=max(ans[i],ans[i+1]);
cnts[i]+=cnts[i+1];
}
for (int i=0;i<n;i++) printf("%lld %lld\n",cnts[i],ans[i]==-inf ? 0 : ans[i]);
return 0;
}

Solution

  原来后缀数组也是可做的。。因为大的height对小的height统计答案并不会影响,所以我们从大到小枚举所有的height,然后并查集维护一下集合大小以及集合中的最大值和最小值。很好理解,看下代码吧。

细节

  比后缀自动机少多了。。

代码

// uoj131
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf 1ll<<60
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std; const int maxn=300010;
int sa[maxn],rank[maxn],height[maxn];
int n,a[maxn],fa[maxn];
LL sz[maxn],mx[maxn],mn[maxn],sum[maxn],ans[maxn];
char s[maxn]; struct data {int w,x,y;}t[maxn]; namespace Suffix {
int wa[maxn],wb[maxn],ww[maxn];
bool cmp(int *r,int a,int b,int l) {
return r[a]==r[b] && r[a+l]==r[b+l];
}
void da(char *r,int *sa,int n,int m) {
int i,j,p,*x=wa,*y=wb;
for (i=0;i<=m;i++) ww[i]=0;
for (i=1;i<=n;i++) ww[x[i]=r[i]]++;
for (i=1;i<=m;i++) ww[i]+=ww[i-1];
for (i=n;i>=1;i--) sa[ww[x[i]]--]=i;
for (p=0,j=1;p<n;j*=2,m=p) {
for (p=0,i=n-j+1;i<=n;i++) y[++p]=i;
for (i=1;i<=n;i++) if (sa[i]>j) y[++p]=sa[i]-j;
for (i=0;i<=m;i++) ww[i]=0;
for (i=1;i<=n;i++) ww[x[y[i]]]++;
for (i=1;i<=m;i++) ww[i]+=ww[i-1];
for (i=n;i>=1;i--) sa[ww[x[y[i]]]--]=y[i];
for (swap(x,y),x[sa[1]]=p=1,i=2;i<=n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j) ? p : ++p;
}
}
void calheight(char *r,int *sa,int n) {
for (int i=1;i<=n;i++) rank[sa[i]]=i;
for (int k=0,i=1;i<=n;i++) {
if (k) k--;
int j=sa[rank[i]-1];
while (r[i+k]==r[j+k]) k++;
height[rank[i]]=k;
}
}
} bool cmp(data a,data b) {
return a.w>b.w;
}
int find(int x) {
return fa[x]==x ? x : fa[x]=find(fa[x]);
}
int main() {
scanf("%d",&n);
scanf("%s",s+1);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
Suffix::da(s,sa,n,300);
Suffix::calheight(s,sa,n);
for (int i=1;i<=n;i++) {
fa[i]=i;sz[i]=1;
mx[i]=mn[i]=a[i];
}
for (int i=2;i<=n;i++) t[i-1]=(data){height[i],sa[i-1],sa[i]};
sort(t+1,t+n,cmp);
for (int i=0;i<n;i++) ans[i]=-inf;
for (int i=1;i<n;i++) {
int x=find(t[i].x),y=find(t[i].y);
if (x!=y) {
fa[x]=y;
sum[t[i].w]+=sz[x]*sz[y];
sz[y]+=sz[x];
ans[t[i].w]=max(ans[t[i].w],max(mx[x]*mx[y],mn[x]*mn[y]));
mx[y]=max(mx[y],mx[x]);
mn[y]=min(mn[y],mn[x]);
}
}
for (int i=n-2;i>=0;i--) {
ans[i]=max(ans[i],ans[i+1]);
sum[i]+=sum[i+1];
}
for (int i=0;i<n;i++) printf("%lld %lld\n",sum[i],sum[i]==0 ? 0 : ans[i]);
return 0;
}

【uoj131】 NOI2015—品酒大会的更多相关文章

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

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

  2. UOJ131 [NOI2015] 品酒大会

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

  3. BZOJ 4199: [Noi2015]品酒大会 [后缀数组 带权并查集]

    4199: [Noi2015]品酒大会 UOJ:http://uoj.ac/problem/131 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品 ...

  4. [UOJ#131][BZOJ4199][NOI2015]品酒大会 后缀数组 + 并查集

    [UOJ#131][BZOJ4199][NOI2015]品酒大会 试题描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个 ...

  5. 洛谷 P2178 [NOI2015]品酒大会 解题报告

    P2178 [NOI2015]品酒大会 题目描述 一年一度的"幻影阁夏日品酒大会"隆重开幕了.大会包含品尝和趣味挑战 两个环节,分别向优胜者颁发"首席品酒家"和 ...

  6. 【BZOJ4199】[Noi2015]品酒大会 后缀数组+并查集

    [BZOJ4199][Noi2015]品酒大会 题面:http://www.lydsy.com/JudgeOnline/wttl/thread.php?tid=2144 题解:听说能用SAM?SA默默 ...

  7. [UOJ#131][BZOJ4199][NOI2015]品酒大会

    [UOJ#131][BZOJ4199][NOI2015]品酒大会 试题描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个 ...

  8. BZOJ_4199_[Noi2015]品酒大会_后缀自动机

    BZOJ_4199_[Noi2015]品酒大会_后缀自动机 Description 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品 酒家”和“首席 ...

  9. [NOI2015]品酒大会(SA数组)

    [NOI2015]品酒大会 题目描述 一年一度的"幻影阁夏日品酒大会"隆重开幕了.大会包含品尝和趣味挑战 两个环节,分别向优胜者颁发"首席品酒家"和" ...

  10. 洛谷P2178 [NOI2015]品酒大会 后缀数组+单调栈

    P2178 [NOI2015]品酒大会 题目链接 https://www.luogu.org/problemnew/show/P2178 题目描述 一年一度的"幻影阁夏日品酒大会" ...

随机推荐

  1. oracle 数据库的详细安装教程

    由于oracle数据库比较大 所以安装的时候比较慢是目前装的最大的软件了吧 而且如果装崩了 可能还会重装系统 不过比较幸运 一次就装好 1.需要去官网下载  https://www.oracle.co ...

  2. 《how tomcat works》阅读笔记 - 2 - 门面设计模式,避免强制转换

    在第二章 2.3节中 try { servlet = (Servlet) myClass.newInstance(); servlet.service((ServletRequest) request ...

  3. Oz 创建Ubuntu镜像

    参考链接: http://blog.csdn.net/gcogle/article/details/52767135http://tlinux.blog.51cto.com/7288656/17497 ...

  4. 2019第十届蓝桥杯C++B组题解(赛后重写的,不确保答案正确性,仅供参考)

    先说一下这次的感受吧,我们考场比较乱,开始比赛了,还有的电脑有故障,(向这些人发出同情),第一次认真参加比赛,真正比赛的时候感觉没有那么正式,很乱,各种小问题,(例如博主就没找到题目在哪里,找到后又不 ...

  5. SQL ser 进行表中的插入操作时,变量字段名,导致报错时解决办法 :动态SQL

    标题不能描述的很清楚,下面具体说所我要描述的问题,和解决的办法. 作为SQL小白一枚,近日在写一段代码,代码如下: 报错显示 变量@vv附近错误. 后来经过了解,原来是因为,这样需要使用 动态SQL去 ...

  6. Python20 - Day09

    python并发编程之多线程理论 1.什么是线程? 进程只是用来把资源集中到一起(进程是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位. 多线程(多个控制线程)的概念是,在一个进程中存在 ...

  7. Bing词典vs有道词典比对测试报告

    功能篇 核心功能测评:http://www.cnblogs.com/C705/p/4075554.html 细节与用户体验:http://www.cnblogs.com/C705/p/4077112. ...

  8. 09慕课网《进击Node.js基础(一)》HTTP-get/request

    get是对request封装 可以在后台发起http请求,获取远程资源,更新或者同步远程资源 http.request(options[,callback]) 以下代码灌水失败: var http = ...

  9. mabatis报错 Result Maps collection already contains value for gamedataserver.dao.one.ChargeRecordMapper.BaseResultMap

    1.解决这种报错看看英文"already ",也就是已经存在,其实是因为存在了两个id相同的返回,以下可以看看,根据这种例子看看自己项目是不是这种问题

  10. 第一个spring冲刺总结

    讨论成员:罗凯旋.罗林杰.吴伟锋.黎文衷 第一阶段总体是做到了运算的功能,只是一些基本的功能实现,包括APP进入动画,以及界面的基本效果设计,还有核心算法已经实现(可以计算括号 乘除法等等)“: 燃尽 ...