題目傳送門
題意:
調酒師 調製了 杯雞尾酒。這 杯雞尾酒排成一行,其中第 杯酒被貼上了一個標籤 ,每杯酒有一個美味值,每個標籤都是 個小寫英文字母之一,把這些酒想象爲一個字符串 。兩個長度都是 的子序列,子序列起始位置分別是 。如果這兩個子序列相等,那就認爲是“ 相似”。這兩個子序列的美味值是 。
問你“ 相似”的種類數和最大美味值。 。
題解:
首先衆所周知:
需要把 想象成 連接 和 的邊。這個很重要,想到這裏就會了。
掏出後綴數組板子,我們就有了 數組。建議使用結構體,存儲權值和連接的點。
然後我們按權值從大到小排列 數組。
逐漸加邊,然後維護所需信息就好了。
說一下怎麼維護信息:
第一個答案是多少種方法可以選出2杯“ r 相似”的酒。這道題比較特殊,因爲原圖是一條鏈,也就是一棵樹,所以每次加邊後就是把兩個連通塊連接成一個連通塊,這樣通過乘法原理,加邊時加上兩個連通塊大小的乘積就好了。
第二個答案是選擇 2 杯“r相似”的酒調兌可以得到的美味度的最大值。假如每杯酒的權值都是非負的,那麼只需要兩個連通塊合併後的最大值乘次大值就好了。現在有負權值,我們還需要考慮最小值和次小值,因爲負負得正。
每次加邊時,只對當前“r相似”維護信息即可,不用考慮“[0,r-1]相似”,最後掃一遍考慮後綴就好了。
感受:
昨天一直硬懟主席樹套線段樹,寫了200多行,自己感覺寫不出來,放棄了。
看了正解,好簡單。
我真菜。
這個思路很巧妙,而且感覺很自然,很好寫。
代碼:
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 3e5 + 5 ;
int rk[maxn << 1] , sa[maxn << 1] , height[maxn << 1] ;
int tmp[maxn << 1] , cnt[maxn] ;
int n , a[maxn] ;
char s[maxn] ;
ll ans1[maxn] , ans2[maxn] ;
int pre[maxn] , f[maxn][4] ;
int siz[maxn] ;
struct node
{
int x , id ;
bool operator < (const node &s) const
{
if(x != s.x) return x > s.x ;
else return id < s.id ;
}
} h[maxn] ;
void suffixarray(int n , int m)
{
n ++ ;
for(int i = 0 ; i < n * 2 + 5 ; i ++)
rk[i] = sa[i] = height[i] = tmp[i] = 0 ;//開2 倍空間
for(int i = 0 ; i < m ; i ++) cnt[i] = 0 ;
for(int i = 0 ; i < n ; i ++) cnt[rk[i] = s[i]] ++ ;
for(int i = 1 ; i < m ; i ++) cnt[i] += cnt[i - 1] ;
for(int i = 0 ; i < n ; i ++) sa[-- cnt[rk[i]]] = i ;
for(int k = 1 ; k <= n ; k <<= 1)
{
int j = 0 ;
for(int i = 0 ; i < n ; i ++)
{
j = sa[i] - k ;
if(j < 0) j += n ;
tmp[cnt[rk[j]] ++] = j ;
}
sa[tmp[cnt[0] = 0]] = j = 0 ;
for(int i = 1 ; i < n ; i ++)
{
if(rk[tmp[i]] != rk[tmp[i - 1]]
|| rk[tmp[i] + k] != rk[tmp[i - 1] + k])
cnt[++ j] = i ;
sa[tmp[i]] = j ;
}
memcpy(rk , sa , n * sizeof(int)) ;
memcpy(sa , tmp , n * sizeof(int)) ;
if(j >= n - 1) break ;
}
height[0] = 0 ;
for(int i = 0 , k = 0 , j = rk[0] ; i < n - 1 ; i ++ , k ++)
while(~k && s[i] != s[sa[j - 1] + k])
height[j] = k -- , j = rk[sa[j] + 1] ;
}
int find(int u)
{
if(pre[u] == u) return u ;
return pre[u] = find(pre[u]) ;
}
void join(int k , int x , int y)
{
int fx = find(x) ;
int fy = find(y) ;
pre[fx] = fy ;
ans1[k] += ll(siz[fx]) * siz[fy] ;
if(f[fx][0] > f[fy][0])
f[fy][1] = f[fy][0] , f[fy][0] = f[fx][0] ;
else
f[fy][1] = max(f[fy][1] , f[fx][0]) ;
if(f[fx][3] < f[fy][3])
f[fy][2] = f[fy][3] , f[fy][3] = f[fx][3] ;
else
f[fy][2] = min(f[fy][2] , f[fx][3]) ;
ll c = max(ll(f[fy][0]) * f[fy][1] , ll(f[fy][2]) * f[fy][3]) ;
ans2[k] = max(ans2[k] , c) ;
siz[fy] += siz[fx] ;
}
int main()
{
scanf("%d" , &n) ;
scanf("%s" , s) ;
for(int i = 0 ; i < n ; i ++) scanf("%d" , &a[i]) ;
suffixarray(n , 200) ;
for(int i = 2 ; i <= n ; i ++)
h[i].x = height[i] , h[i].id = i ;
sort(h + 2 , h + n + 1) ;
for(int i = 0 ; i < n ; i ++)
{
pre[i] = i ;
siz[i] = 1 ;
f[i][0] = a[i] ;
f[i][1] = -2e9 ;
f[i][2] = 2e9 ;
f[i][3] = a[i] ;
}
for(int i = 0 ; i <= n ; i ++)
ans2[i] = -1e18 ;
for(int i = 2 ; i <= n ; i ++)
join(h[i].x , sa[h[i].id - 1] , sa[h[i].id]) ;
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 - 1 ; i ++)
{
if(ans1[i] == 0) printf("0 0\n") ;
else
printf("%lld %lld\n" , ans1[i] , ans2[i]) ;
}
return 0 ;
}