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

【BZOJ4199】[Noi2015]品酒大会

题面http://www.lydsy.com/JudgeOnline/wttl/thread.php?tid=2144

题解:听说能用SAM?SA默默水过~

本题的实现还是非常简单的,先求出height数组,然后两杯酒‘r‘相似就等价于二者中间的height都>=r,于是我们将height排序,从大到小扔进去,那么所有连续的区间中的点对就都是相似的了。维护连续区间可以用并查集。统计点对个数需要维护size,统计最大乘积需要维护最(次)大(小)值,然后没了~

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=300010;
int n,m;
typedef long long ll;
int sa[maxn],st[maxn],ra[maxn],rb[maxn],r[maxn],h[maxn],rank[maxn];
int f[maxn],q[maxn],v[maxn];
ll ans[maxn],sum[maxn],m1[maxn],m2[maxn],n1[maxn],n2[maxn],siz[maxn];
char str[maxn];
void work()
{
	int i,j,p,k,*x=ra,*y=rb;
	for(i=0;i<n;i++)	st[x[i]=r[i]]++;
	for(i=1;i<m;i++)	st[i]+=st[i-1];
	for(i=n-1;i>=0;i--)	sa[--st[x[i]]]=i;
	for(j=p=1;p<n;j<<=1,m=p)
	{
		for(p=0,i=n-j;i<n;i++)	y[p++]=i;
		for(i=0;i<n;i++)	if(sa[i]>=j)	y[p++]=sa[i]-j;
		for(i=0;i<m;i++)	st[i]=0;
		for(i=0;i<n;i++)	st[x[y[i]]]++;
		for(i=1;i<m;i++)	st[i]+=st[i-1];
		for(i=n-1;i>=0;i--)	sa[--st[x[y[i]]]]=y[i];
		for(swap(x,y),x[sa[0]]=0,i=p=1;i<n;i++)
			x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j])?p-1:p++;
	}
	for(i=1;i<n;i++)	rank[sa[i]]=i;
	for(i=k=0;i<n-1;h[rank[i++]]=k)
		for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
int find(int x)
{
	return (f[x]==x)?x:(f[x]=find(f[x]));
}
bool cmp(int a,int b)
{
	return h[a]>h[b];
}
int main()
{
	scanf("%d%s",&n,str);
	int i,j,a,b;
	for(i=0;i<n;i++)	r[i]=str[i]-‘a‘+1;
	for(i=0;i<n;i++)	scanf("%d",&v[i]);
	m=27,n++,work(),n--;
	for(i=1;i<=n;i++)	q[i]=f[i]=i,m1[i]=n1[i]=v[sa[i]],m2[i]=-1ll<<60,n2[i]=1ll<<60,siz[i]=1;
	sort(q+2,q+n+1,cmp);
	ans[n]=-1ll<<60;
	for(i=2,j=n-1;j>=0;j--)
	{
		ans[j]=ans[j+1],sum[j]=sum[j+1];
		for(;h[q[i]]==j&&i<=n;i++)
		{
			a=find(q[i]-1),b=find(q[i]);
			if(m1[a]>m1[b])	m2[b]=max(m1[b],m2[a]),m1[b]=m1[a];
			else	m2[b]=max(m2[b],m1[a]);
			if(n1[a]<n1[b])	n2[b]=min(n1[b],n2[a]),n1[b]=n1[a];
			else	n2[b]=min(n2[b],n1[a]);
			ans[j]=max(ans[j],max(n1[b]*n2[b],m1[b]*m2[b]));
			sum[j]+=siz[a]*siz[b];
			siz[b]+=siz[a],f[a]=b;
		}
	}
	for(i=0;i<n;i++)	printf("%lld %lld\n",sum[i],ans[i]==ans[n]?0:ans[i]);
	return 0;
}
时间: 2024-10-29 10:45:38

【BZOJ4199】[Noi2015]品酒大会 后缀数组+并查集的相关文章

[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 个标签顺次连接构成的字

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

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

【BZOJ-4199】品酒大会 后缀数组 + 并查集合并集合

4199: [Noi2015]品酒大会 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 436  Solved: 243[Submit][Status][Discuss] Description 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainbow 调制了 nn 杯鸡尾酒.这 nn 杯鸡尾酒排成一行,其中第 i

Uoj #131. 【NOI2015】品酒大会 后缀数组,并查集

#131. [NOI2015]品酒大会 统计 描述 提交 自定义测试 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainbow 调制了 nn 杯鸡尾酒.这 nn 杯鸡尾酒排成一行,其中第 ii 杯酒 (1≤i≤n1in) 被贴上了一个标签 sisi,每个标签都是 2626 个小写英文字母之一.设 Str(l,r)Strlr 表示第 ll 杯酒到第 rr 杯酒的 

[bzoj4199][Noi2015]品酒大会——后缀数组

题目大意: 给定一个序列,定义两个后缀是k相似的当且仅当这两个后缀有长度为k的公共前缀. 求对任意\(r\in [0,n-1]\),\(r\)相似的后缀的对数和两个后缀乘积的最大值. 思路: 先考虑后缀数组是如何计算两个后缀的lcp,发现是对于一段连续的height取min. 于是对于制定的相似度r,height < r的位置必定是两个后缀不能越过的,于是不难发现将有height \(\ge\)r的位置给取出来,然后整个序列分成了若干个连通块,一对具有r相似的后缀必定同时在一个联通快里面. 对于

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 #

Uva 12361 File Retrieval 后缀数组+并查集

题意:有F个单词,1 <= F <=60 , 长度<=10^4, 每次可以输入一个字符串,所有包含该字串的单词会形成一个集合. 问最多能形成多少个不同的集合.集合不能为空. 分析:用后缀数组处理.然后首先考虑一个单词形成一个集合的情况,若该单词是其他单词的字串,则该单词显然不会形成一个集合,那么利用后缀数组, 对于每个单词看能否与其他单词有LCP,且LCP 长度为该单词本身长度. 然后就是多个单词形成集合的情况:比较简单的处理方式就是将h数组值相同的下标集中存储,比如h[x] = h[y

loj6198谢特 后缀数组+并查集+Trie

先把问题放在后缀数组上考虑 已知两个数组a b,求min(a[i],...,a[j])+(b[i]^b[j])的最大值 套路题 初始每个点都是一个小连通块 把a按从大到小的顺序加入,计算当前加入边作为min的贡献: 每次加入会把两个连通块联通,答案就是两边连通块各出一个数能得到的异或和最大值 我:这不是线性基吗 miaom:mdzz,只能有两个数 我:蛤,好难啊,怎么做啊 miaom:Trie啊 我:哦 没了 1 #include <bits/stdc++.h> 2 #define N 500

POJ 3415 Common Substrings 后缀数组+并查集

后缀数组,看到网上很多题解都是单调栈,这里提供一个不是单调栈的做法, 首先将两个串 连接起来求height   求完之后按height值从大往小合并.  height值代表的是  sa[i]和sa[i-1] 的公共前缀长度,那么每次合并就是合并  i和i-1 那么在合并小的时候公共前缀更大的肯定已经都合并在一起,那么就可以直接统计了. #include<iostream> #include<cstdio> #include<algorithm> #include<