UOJ #131 【NOI2015】 品酒大会

题目链接:品酒大会

  学了后缀自动机之后再来写这道题就轻松多了……

  首先,题面中的两杯酒\(r\)相似就是这两个后缀的最长公共前缀大于等于\(r\)。把串翻转过来之后就变成了两个前缀的最长公共后缀……然后就是\(parent\)树的事了……

  接着,我们要求出选出两杯\(r\)相似的酒的方案数。这个比较显然,就是枚举两个节点的\(lca\),然后子树的\(right\)集合大小 两两乘起来就可以了。

  然后就是选出两个值使得乘积最大。树型\(dp\)还是比较显然的,在\(parent\)树上记录一下每棵子树内的最大值、最小值,那么显然最大值要么就是最大值和最大值乘起来,要么就是最小值和最小值乘起来,要么就是子树内的最大值。背包一下就求出来了。由于这道题每次是更新一段前缀,于是把更新弄成点事件,\(dp\)完后再从大到小扫一遍即可。要注意负数的情况。

  下面贴代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define maxn 600010
#define INF (1LL<<61)
#define maxint (1<<30)

using namespace std;
typedef long long llg;

char s[maxn>>1];
int n,tot,la,head[maxn],next[maxn],to[maxn],tt,a[maxn],nn;
int son[maxn][26],len[maxn],fa[maxn],siz[maxn],pos[maxn];
llg ans[maxn],maxv[maxn],minv[maxn],f[maxn],c[maxn];

int getint(){
	int w=0;bool q=0;
	char c=getchar();
	while((c>‘9‘||c<‘0‘)&&c!=‘-‘) c=getchar();
	if(c==‘-‘) c=getchar(),q=1;
	while(c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar();
	return q?-w:w;
}

void link(int x,int y){to[++tt]=y;next[tt]=head[x];head[x]=tt;}
void insert(int p,int x){
	int np=++tot; siz[np]=1; len[np]=len[p]+1;
	pos[np]=a[++nn]; f[np]=-INF;
	for(;p && !son[p][x];p=fa[p]) son[p][x]=np;
	if(!p) fa[np]=1;
	else{
		int q=son[p][x];
		if(len[q]==len[p]+1) fa[np]=q;
		else{
			int nq=++tot;
			memcpy(son[nq],son[q],sizeof(son[q]));
			fa[nq]=fa[q]; fa[np]=fa[q]=nq; len[nq]=len[p]+1;
			for(;son[p][x]==q;p=fa[p]) son[p][x]=nq;
		}
	}
	la=np;
}

void dfs(int u){
	if(siz[u]) maxv[u]=minv[u]=pos[u];
	for(int i=head[u],v;v=to[i],i;i=next[i]){
		dfs(v); ans[len[u]]+=(llg)siz[u]*siz[v];
		if(siz[u]) f[u]=max(f[u],max(maxv[u]*maxv[v],minv[u]*minv[v]));
		maxv[u]=max(maxv[u],maxv[v]); minv[u]=min(minv[u],minv[v]);
		siz[u]+=siz[v]; f[u]=max(f[u],f[v]);
	}
	if(siz[u]>1) c[len[u]]=max(c[len[u]],f[u]);
}

int main(){
	File("a");
	scanf("%d",&n); scanf("%s",s+1); tot=la=1;
	for(int i=1;i<=n;i++) a[n-i+1]=getint();
	for(int i=n>>1;i;i--) swap(s[i],s[n-i+1]);
	for(int i=1;i<=n;i++) insert(la,s[i]-‘a‘);
	for(int i=0;i<=n;i++) c[i]=-INF;
	for(int i=2;i<=tot;i++) link(fa[i],i);
	for(int i=1;i<=tot;i++) minv[i]=maxint,maxv[i]=-maxint,f[i]=-INF;
	dfs(1);
	for(int i=n-1;i>=0;i--) c[i]=max(c[i],c[i+1]),ans[i]+=ans[i+1];
	for(int i=0;i<n;i++){
		if(!ans[i]) c[i]=0;
		printf("%lld %lld\n",ans[i],c[i]);
	}
	return 0;
}
时间: 2024-12-14 11:52:01

UOJ #131 【NOI2015】 品酒大会的相关文章

●UOJ 131 [NOI2015] 品酒大会

题链: http://uoj.ac/problem/131 题解: 网上大多数的方法都是用并查集维护.这里呢,给出另一种自己YY的解法(但实际上本质差不多吧): 后缀数组,RMQ,单调栈 1).预处理 1].首先对字符串后缀排序,得到 sa[i],rank[i],height[i]    2].然后维护出 L[i]:表示在后缀数组中,排名最小(记其排名为 L[i])的后缀与排名为 i的后缀的LCP>=hei[i]    同理 R[i]:表示在后缀数组中,排名最大(记其排名为 R[i])的后缀与排

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

[UOJ#131][BZOJ4199][NOI2015]品酒大会 试题描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainbow 调制了 n杯鸡尾酒.这 n杯鸡尾酒排成一行,其中第 i杯酒 (1≤i≤n ) 被贴上了一个标签 si ,每个标签都是 26 个小写英文字母之一.设 Str(l,r)表示第 l杯酒到第 r 杯酒的 r−l+1 个标签顺次连接构成的字

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

[BZOJ4199][Noi2015]品酒大会 题面:http://www.lydsy.com/JudgeOnline/wttl/thread.php?tid=2144 题解:听说能用SAM?SA默默水过~ 本题的实现还是非常简单的,先求出height数组,然后两杯酒'r'相似就等价于二者中间的height都>=r,于是我们将height排序,从大到小扔进去,那么所有连续的区间中的点对就都是相似的了.维护连续区间可以用并查集.统计点对个数需要维护size,统计最大乘积需要维护最(次)大(小)值,

【uoj131】 NOI2015—品酒大会

http://uoj.ac/problem/131 (题目链接) 题意 给出一个字符串,每个后缀有一个权值${a_i}$,这些后缀两两之间存在公共前缀.问能够组成长度从0~n-1的公共前缀的后缀的方案数以及他们权值的最大乘积. Solution 听LCF说这是水题,就来做了.. lyp学长说SAM构出来后就两个东西:在自动机上dp,在后缀树上搞事情. 于是我们很容易想到在后缀树上dp(感觉在自动机上dp没什么卵用),于是我们把串反过来见后缀自动机,然后建出后缀树,在上面树形dp就可以了. 假设当

BZOJ 4199 [Noi2015]品酒大会:后缀数组 + 并查集

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4199 题意: 给你一个长度为n的字符串s,和一个长为n的数组v. 对于每个整数r∈[0,n-1]: (1)问你有多少对后缀(suffix(i), suffix(j)),满足LCP(suffix(i), suffix(j)) >= r (2)输出mul[r] = max(v[i]*v[j]),其中i,j满足(1)的条件 题解: 先考虑第(1)问. 由于LCP只受连续的一段height中最小

BZOJ 4199: [Noi2015]品酒大会( 后缀数组 + 并查集 )

求出后缀数组后, 对height排序, 从大到小来处理(r相似必定是0~r-1相似), 并查集维护. 复杂度O(NlogN + Nalpha(N)) ----------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> using namespace std; ty

字符串(后缀数组||SAM):NOI2015 品酒大会

这道题可以转化为计数类问题. 若使用后缀数组,那么答案就是所有位置二元组(i,j)的lcp对0~lcp答案段的贡献.然后发现若一个二元组有x的贡献,那么对x-1有同样的贡献,考虑先求出lcp(max)的答案,再传给lcp(max-1)等等,复杂度是O(N)的. 若用SAM,那么需要求的答案在x与fa[x]的转移之间,因为后缀自动机中每个点所代表的出现位置和出现次数是相等的,可以合并,事实上还是用到了后缀数组的叠加的原理,从叶子节点一路传上去…………………………………………… 还有要注意INF要足

BZOJ4199: [Noi2015]品酒大会

后缀数组height排序后并查集合并 也就是height较大的合并不影响较小的 num[i]=num[i+1]  ans[i]=ans[i+1] 合并时,num+=sz[x]*sz[y],ans=max(mn[x]*mn[y],mx[x]*mx[y],ans) 这种思路适应于求点对,还可以考虑启发式合并 后缀数组还有的常见思路就是二分,height分组 1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 300005 4 #

Bzoj4199:[NOI2015]品酒大会

题面 Bzoj4199 Sol 后缀数组 显然的暴力就是求\(LCP\)+差分 \(40\)分 # include <bits/stdc++.h> # define RG register # define IL inline # define Fill(a, b) memset(a, b, sizeof(a)) using namespace std; typedef long long ll; const int _(3e5 + 5); IL int Input(){ RG int x =

bzoj4199: [Noi2015]品酒大会 (并查集 &amp;&amp; 后缀数组)

据说用后缀自动机 + dp也能做 然而并不会 后缀数组的做法呢 就是先建个后缀数组,求出height值,此时如果直接找,复杂度是n ^ 2的,肯定会超时. 但是height大的值是不会对小的产生影响的,所以可以按height大小,从大到小合并两个区间,用并查集维护就可以了 代码如下 1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 using namespace std; 5 const