4199: [Noi2015]品酒大会

bzoj上没有题面。。(百度有hhh)

这看上去就很后缀数组啊。。

先处理出后缀数组(都快忘了咋整了)。。

也就是处理出sa,rank,height三个数组

由后缀数组的性质,两个后缀的LCP是路径上height值取min

第i个位置的height值,能用作LCP的区间[L,R],满足区间最小height为height[i]

那么在[L,i)中选一位置为左端点,在[i,R]中选一位置作右端点,LCP = height[i]

可以用单调队列求出每个位置能使用的区间[L,R]

小心重复计算

可以这样,对于位置i,使劲向右找,直到找到一个位置k,height[k] < height[i],用k - 1作右端点

对于位置i,使劲向左找,直到找到一个位置k,height[k] <= height[i],用k + 1作左端点

处理出了所有的L[i],R[i],第一个询问就简单了,乘一下即可

第二个询问,可以查询出左边的最大最小值,有边的最大最小值,瞎逼乘一乘,

苟蒻强行套一个RMQ,其实可以线性,不过这样好写。。。

于是常数巨大233

此题边界略啰嗦。。。。调了有点久

#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<map>
#include<set>
#include<cmath>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int maxn = 3E5 + 30;
typedef long long LL;

int n,top,s[maxn],sa[maxn],rank[maxn],Min[maxn][20],Max[maxn][20]
	,L[maxn],R[maxn],height[maxn],t[maxn],t2[maxn],c[maxn];
LL a1[maxn],a2[maxn],mi[2],ma[2],a[maxn],b[maxn];
char ch[maxn];

void Getsa()
{
	int *x = t,*y = t2,m = 26;
	for (int i = 1; i <= n; i++) ++c[x[i] = ch[i]];
	for (int i = 2; i <= m; i++) c[i] += c[i-1];
	for (int i = n; i; i--) sa[c[x[i]]--] = i;
	for (int k = 1; k < n; k <<= 1) {
		int p = 0;
		for (int i = n; i > n - k; i--) y[++p] = i;
		for (int i = 1; i <= n; i++) if (sa[i] - k > 0) y[++p] = sa[i] - k;
		for (int i = 1; i <= m; i++) c[i] = 0;
		for (int i = 1; i <= n; i++) ++c[x[y[i]]];
		for (int i = 2; i <= m; i++) c[i] += c[i-1];
		for (int i = n; i; i--) sa[c[x[y[i]]]--] = y[i];
		swap(x,y); p = 1; x[sa[1]] = 1;
		for (int i = 2; i <= n; i++) 
			x[sa[i]] = y[sa[i]] == y[sa[i-1]] && y[sa[i]+k] == y[sa[i-1]+k]?p:++p;
		m = p;
		if (m >= n) return;
	}
}

void Rank_and_Height()
{
	for (int i = 1; i <= n; i++) rank[sa[i]] = i;
	int k = 0;
	for (int i = 1; i <= n; i++) {
		if (k) --k;
		while (ch[i+k] == ch[sa[rank[i]-1]+k]) ++k;
		height[rank[i]] = k;
	}
}

void Query(int l,int r,int k)
{
	if (l > r) return;
	int len = r - l + 1,t = 0;
	while (l + (1<<t) - 1 <= r) ++t; --t;
	mi[k] = min(Min[l][t],Min[r - (1<<t) + 1][t]);
	ma[k] = max(Max[l][t],Max[r - (1<<t) + 1][t]);
}

void Pre_Work()
{
	for (int i = 1; i <= n; i++)
		b[i] = Min[i][0] = Max[i][0] = a[sa[i]];
	for (int j = 1; j < 20; j++)
		for (int i = 1; i <= n; i++) {
			int t = i + (1<<(j-1));
			if (t > n) break;
			Max[i][j] = max(Max[i][j-1],Max[t][j-1]);
			Min[i][j] = min(Min[i][j-1],Min[t][j-1]);
		}
}

void Print()
{
	for (int i = 1; i <= n; i++) {
		for (int j = sa[i]; j <= n; j++) 
			putchar(ch[j] + 'a' - 1);
		puts("");
	}
}

LL getLL()
{
	char ch = getchar();
	LL ret = 0,A = 1;
	while (ch < '0' || '9' < ch) {
		if (ch == '-') A = -1;
		ch = getchar();
	}
	while ('0' <= ch && ch <= '9') {
		ret = ret*10LL + 1LL*(ch - '0');
		ch = getchar();
	}
	return ret*A;
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
		//freopen("test.txt","w",stdout);
	#endif
	
	cin >> n;
	scanf("%s",ch + 1);
	for (int i = 1; i <= n; i++) 
		a[i] = getLL(),ch[i] -= ('a' - 1);
	Getsa();
	Rank_and_Height();
	//Print();
	s[top = 1] = 1;
	height[1] = 0; 
	height[n + 1] = -1;
	for (int i = 2; i <= n + 1; i++) {
		while (top && height[s[top]] > height[i]) 
			R[s[top]] = i - 1,--top;
		s[++top] = i;
	}
	s[top = 1] = n; L[1] = 1;
	for (int i = n - 1; i; i--) {
		while (top && height[s[top]] >= height[i])
			L[s[top]] = i + 1,--top;
		s[++top] = i;
	}
	Pre_Work();
	for (int i = 1; i <= n; i++) a2[i] = -2E18;
	for (int i = 2; i <= n; i++) {
		LL A = i - L[i] + 1;
		LL B = R[i] - i + 1;
		a1[height[i]] += A*B;
		Query(L[i] - 1,i - 1,0); Query(i,R[i],1);
		LL Ma = -2E18;
		Ma = max(Ma,mi[0]*mi[1]);
		Ma = max(Ma,ma[0]*ma[1]);
		Ma = max(Ma,mi[0]*ma[1]);
		Ma = max(Ma,ma[0]*mi[1]);
		a2[height[i]] = max(a2[height[i]],Ma);
	}
	for (int i = n - 1; i >= 0; i--)
		a1[i] += a1[i + 1],a2[i] = max(a2[i],a2[i+1]);
	for (int i = 0; i < n; i++) 
		if (!a1[i]) puts("0 0");
		else printf("%lld %lld\n",a1[i],a2[i]);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值