题目描述(转自百度文库)
一年一度的“幻影阁夏日品酒大会”隆重开幕了。大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加。
在大会的晚餐上,调酒师Rainbow调制了 ? 杯鸡尾酒。这 n 杯鸡尾酒排成一行,其中第 ? 杯酒 (1≤?≤?) 被贴上了一个标签 ?? ,每个标签都是 26 个小写英文字母之一。设 ???(?,?)表示第 ? 杯酒到第 ? 杯酒的 ?−?+1 个标签顺次连接构成的字符串。若 ???(?,??)=???(?,??),其中 1≤?≤??≤?,1≤?≤??≤?,?≠?,??−?+1=??−?+1=? ,则称第 ? 杯酒与第 ? 杯酒是“ ? 相似”
的。当然两杯“ ? 相似”(?>1)的酒同时也是“ 1 相似”、“ 2 相似”、……、“ (?−1)相似”的。特别地,对于任意的 1≤?,?≤? , ?≠? ,第 ? 杯酒和第 ? 杯酒都是“ 0 相似”的。
在品尝环节上,品酒师Freda轻松地评定了每一杯酒的美味度,凭借其专业的水准和经验成功夺取了“首席品酒家”的称号,其中第 ? 杯酒 (1≤?≤?) 的美味度为 ?? 。现在Rainbow公布了挑战环节的问题:本次大会调制的鸡尾酒有一个特点,如果把第 ? 杯酒与第 ? 杯酒调兑在一起,将得到一杯美味度为 ???? 的酒。现在请各位品酒师分别对于 ?=0,1,2,⋯,?−1 ,统计出有多少种方法可以选出 2 杯“ ? 相似”的酒,并回答选择 2 杯“ ? 相似”的酒调兑可以得到的美味度的最大值。
输入
输入文件的第1行包含1个正整数 ? ,表示鸡尾酒的杯数。
第 2 行包含一个长度为 ? 的字符串 ? ,其中第 ? 个字符表示第 ? 杯酒的标签。第 3 行包含 ? 个整数,相邻整数之间用单个空格隔开,其中第 ? 个整数表示第 ? 杯酒的美味度 ?? 。
输出
输出文件包括 ? 行。第 ? 行输出 2 个整数,中间用单个空格隔开。第 1 个整数表示选出两杯“ (?−1) 相似”的酒的方案数,第 2 个整数表示选出两杯“(?−1) 相似”的酒调兑可以得到的最大美味度。若不存在两杯“ (?−1) 相似”的酒,这两个数均为 0 。
样例输入
10
ponoiiipoi
2 1 4 7 4 8 3 6 4 7
样例输出
45 56
10 56
3 32
0 0
0 0
0 0
0 0
0 0
0 0
0 0
题解
后缀自动机求后缀树+树形dp
首先要知道后缀自动机的一个性质:后缀自动机的parent树是反串的后缀树,也即反串的后缀自动机的parent树是后缀树。
所以我们可以把原串反过来加入到后缀自动机中,求出原串的后缀树。
而后缀树是一棵压缩后的Trie树,Trie树又有性质:两个节点lca的深度(字符串长度)是两个串的公共前缀长度。
根据这个我们可以树形dp。
设ans1[x]、ans2[x]表示对应最大相似为x时的两种答案。
那么当枚举到深度为x的点i时,应将其自身与子树的两两组合统计到ans1中,可以在每次si[i]+=si[son]时更新答案。
而由于有负数的存在,ans2的情况比较复杂。考虑可能成为答案的只有 最大值×次大值 和 最小值×次小值 ,同样在maxn=max(maxn[i],maxn[son])时更新答案。
最后,我们求出的时最大相似,也即最长公共前缀,实际上小于等于这个值都可以。所以我们还需要求一下后缀和以及后缀最大值,具体见代码。
最后的最后,必须要吐槽一下:博客园的编辑器太辣鸡了,随便粘贴一点东西要调好长时间的格式(这也是我没有从uoj粘贴题目的原因,因为博客园粘贴latex会出错),这点真的好气啊QAQ
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 600010
#define MAX 0x7f7f7f7f
#define MIN 0x80808080
using namespace std;
int w[N] , a[N][26] , fa[N] , dis[N] , last = 1 , tot = 1 , head[N] , to[N] , next[N] , cnt , si[N] , maxn[N] , minn[N];
long long ans1[N] , ans2[N];
char str[N];
void add(int x , int y)
{
to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void ins(int c , int v)
{
int p = last , np = last = ++tot;
dis[np] = dis[p] + 1 , maxn[np] = minn[np] = v , si[np] = 1;
while(p && !a[p][c]) a[p][c] = np , p = fa[p];
if(!p) fa[np] = 1;
else
{
int q = a[p][c];
if(dis[q] == dis[p] + 1) fa[np] = q;
else
{
int nq = ++tot;
memcpy(a[nq] , a[q] , sizeof(a[q])) , dis[nq] = dis[p] + 1 , fa[nq] = fa[q] , fa[np] = fa[q] = nq;
while(p && a[p][c] == q) a[p][c] = nq , p = fa[p];
}
}
}
void dfs(int x)
{
int i , p = dis[x];
for(i = head[x] ; i ; i = next[i])
{
dfs(to[i]);
ans1[p] += (long long)si[x] * si[to[i]];
if(maxn[x] != MIN) ans2[p] = max(ans2[p] , (long long)maxn[x] * maxn[to[i]]);
if(minn[x] != MAX) ans2[p] = max(ans2[p] , (long long)minn[x] * minn[to[i]]);
si[x] += si[to[i]] , maxn[x] = max(maxn[x] , maxn[to[i]]) , minn[x] = min(minn[x] , minn[to[i]]);
}
}
int main()
{
int n , i;
scanf("%d%s" , &n , str + 1);
memset(maxn , 0x80 , sizeof(maxn)) , memset(minn , 0x7f , sizeof(minn)) , memset(ans2 , 0x80 , sizeof(ans2));
for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &w[i]);
for(i = n ; i >= 1 ; i -- ) ins(str[i] - 'a' , w[i]);
for(i = 2 ; i <= tot ; i ++ ) add(fa[i] , i);
dfs(1);
for(i = n - 2 ; i >= 0 ; i -- ) ans1[i] += ans1[i + 1] , ans2[i] = max(ans2[i] , ans2[i + 1]);
for(i = 0 ; i < n ; i ++ ) printf("%lld %lld\n" , ans1[i] , ans1[i] ? ans2[i] : 0);
return 0;
}