P2178 [NOI2015] 品酒大会
题意简述
有一个长度为 \(n\) 的字符串 \(S\),其中第 \(i\) 个字符对应的权值为 \(a_i\)。
设 \(str(l,r)\) 表示 \(S\) 中第 \(l\) 个字符到第 \(r\) 个字符构成的字符串,我们定义第 \(p\) 个字符和第 \(q\) 个字符相似度为 \(r\) 当且仅当 \(str(p,p + r - 1) = str(q,q + r - 1)\),且 \(p \not = q\)。注意,对于同一对位置上的字符 \(p,q\),其相似度可以有多个,也就是说如果其最大的相似度为 \(r\),我们可以认为 \(p,q\) 的相似度为 \([0,r]\) 中的任意一整数。特别地,我们认为任意一个字符对 \(p,q\),在满足 \(p \not = q\) 的前提下,其相似度都可以为 \(0\)。
现在的问题就是,请你求出相似度为 \(0,1,2,…,n - 1\) 的字符对的数量,以及相似度为 \(0,1,2,…,n - 1\) 的所有字符对的权值的最大值,如果不存在相似度为 \(r\) 的字符对,其最大值为 \(0\)。字符对 \(p,q\) 的权值的定义为 \(a_p \times a_q\)。
数据范围与约定:\(n \leq 3 \times 10^5, a \in [-10^9,10^9]\),\(S\) 仅由小写字母构成。
解题思路
思路有点像我之前写的一篇题解:P6503 [COCI2010-2011#3] DIFERENCIJA。
先对 \(S\) 进行后缀排序,求出其 \(height\) 数组,对于任意的 \(p,q(p \not = q)\),满足 \(rk[p] < rk[q]\),那么 \(p,q\) 相似度的最大值 \(r = \min_{i = rk[p] + 1}^{rk[q]} height[i]\)。但是直接这样做的复杂度是 \(O(n^2)\),显然过不了,于是我们尝试对其进行优化。
由上可知,\(p,q\) 的 \(r\) 值的最大值取决于其在 \(height\) 上对应的区间的最小值,所以我们可以尝试在 \(height\) 上统计答案。
我们从区间 \([1,n]\) 开始,对于当前的区间 \([l,r]\),我们找到 \(height\) 在 \([l + 1,r]\) 上的最小值的位置 \(k\),那么对于每一个字符对 \(p,q\),若满足 \(p \in [l,k),q \in[k,r]\),其相似度的最大值 \(r = height[k]\)。所以对于问题一,设 \(ans_i\) 表示相似度为 \(i\) 的字符对的数量,其对 \(ans_0,…,ans_r\) 的贡献就是 \((k - l) \times (r - k + 1)\),这里可以用前缀和来统计。处理完区间 \([l,r]\) 后,再对 \([l,k - 1]\) 和 \([k,r]\) 区间进行相同方式的处理,如此递归下去,直到 \(l \geq r\) 后退出即可。
接下来考虑第二问,我们可以沿用第一问的方法来更新答案。对于当前区间 \([l,r]\) 以及最小的 \(height\) 值的位置 \(k\),我们只需要求出 \([l,k)\) 区间中的点对应的权值和 \([k,r]\) 区间中的点对应的权值的乘积的最大值即可,注意由于进行了后缀排序,所以要重新对应每个点对应的权值。由于 \(a\) 的值可以为负数,所以我们要分别求出 \([l,k)\) 和 \([k,r]\) 区间的权值最小值、权值最大值以及权值的绝对值的最小值,然后把它们分别相乘取 \(\max\) 即可,更新答案的时候就是更新 \(0\) 到 \(height[k]\) 的答案。
具体实现可以看代码。
代码实现
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int MN = 3e5;
int N,A[MN + 5];
char S[MN + 5];
int SA[MN + 5],RK[MN + 5];
int CNT[MN + 5],HP1[MN + 2 << 1],HP2[MN + 2 << 1];
int HG[MN + 5],B[MN + 5];
class Spare_Table{
private:
int peak[MN + 5][20];
function<int(int,int)> mxn;
int Log2(int num){
int res = -1;
while(num > 0)
num >>= 1,++res;
return res;
}
public:
void Init(int *num,int len,const function<int(int,int)> &cmp){
for(int i = 1;i <= len;++i)
peak[i][0] = num[i];
mxn = cmp;
for(int k = 1;k < 20;++k)
for(int i = len - (1 << k) + 1;i > 0;--i)
peak[i][k] = mxn(peak[i][k - 1],peak[i + (1 << k - 1)][k - 1]);
return;
}
int Peak(int l,int r){
int lg2 = Log2(r - l + 1);
return mxn(peak[l][lg2],peak[r - (1 << lg2) + 1][lg2]);
}
}ST1,ST2[3];//最小\最大\绝对值最小
long long ANS1[MN + 5],ANS2[MN + 5];
void Get_ANS(int l,int r);
int main(){
scanf("%d%s",&N,S + 1);
for(int i = 1;i <= N;++i)
scanf("%d",&A[i]);
int base = 128,*x = HP1,*y = HP2;
for(int i = 1;i <= N;++i)
++CNT[(x[i] = S[i])];
for(int i = 1;i <= base;++i)
CNT[i] += CNT[i - 1];
for(int i = N;i > 0;--i)
SA[CNT[x[i]]--] = i;
for(int k = 1;k <= N;k <<= 1){
int rk = 0;
for(int i = N - k + 1;i <= N;++i)
y[++rk] = i;
for(int i = 1;i <= N;++i)
if(SA[i] > k)
y[++rk] = SA[i] - k;
memset(CNT,0,sizeof(CNT));
for(int i = 1;i <= N;++i)
++CNT[x[i]];
for(int i = 1;i <= base;++i)
CNT[i] += CNT[i - 1];
for(int i = N;i > 0;--i)
SA[CNT[x[y[i]]]--] = y[i];
base = 1,swap(x,y);
for(int i = 1;i <= N;++i){
x[SA[i]] = base;
if(y[SA[i]] != y[SA[i + 1]] || y[SA[i] + k] != y[SA[i + 1] + k])
++base;
}
if(base > N)
break;
}
for(int i = 1;i <= N;++i)
RK[SA[i]] = i,B[i] = A[SA[i]];
for(int i = 1,k = 0;i <= N;++i){
if(k > 0)
--k;
int other = SA[RK[i] - 1];
while(S[i + k] == S[other + k])
++k;
HG[RK[i]] = k;
}
for(int i = 1;i <= N;++i)
HP1[i] = i,ANS2[i] = -2e18;
ST1.Init(HP1,N,[&](int a,int b) -> int {return HG[a] < HG[b] ? a : b;});
ST2[0].Init(B,N,[&](int a,int b) -> int {return a < b ? a : b;});
ST2[1].Init(B,N,[&](int a,int b) -> int {return a > b ? a : b;});
ST2[2].Init(B,N,[&](int a,int b) -> int {return abs(a) < abs(b) ? a : b;});
Get_ANS(1,N);
for(int i = N - 1;i >= 0;--i){
ANS1[i] += ANS1[i + 1];
ANS2[i] = max(ANS2[i],ANS2[i + 1]);
}
for(int i = 0;i < N;++i)
printf("%lld %lld\n",ANS1[i],ANS2[i] *= (ANS1[i] != 0));
return 0;
}
void Get_ANS(int l,int r){
if(l >= r)
return;
int mid = ST1.Peak(l + 1,r);
ANS1[HG[mid]] += (long long)(mid - l) * (r - mid + 1);
for(int i = 0;i < 3;++i)
ANS2[HG[mid]] = max(ANS2[HG[mid]],(long long)ST2[i].Peak(l,mid - 1) * ST2[i].Peak(mid,r));
Get_ANS(l,mid - 1),Get_ANS(mid,r);
return;
}
浙公网安备 33010602011771号