[NOI 2015] 品酒大会

[题目链接]

         https://www.lydsy.com/JudgeOnline/problem.php?id=4199

[算法]

         首先 , 题目中有一条性质 :

         若两个子串是“r相似”的 , 那么它们同样是(r - 1)相似 ,(r - 2)相似 , ... , 0相似的

         不妨考虑构建给定字符串的后缀数组 , 将所有后缀按height值排序 , 用并查集合并答案

         并查集维护集合的大小 , 集合内的最大 , 次大权值与最小 , 次小权值

         每次合并两个集合x , y , 假设它们最多是"z相似"的

         那么就会对ans1[0..z]产生size(x) * size(y)的贡献

         同样会对ans2[0..2]产生max{ max1 * max2 , min1 * min2 }的贡献

         用差分前缀和求出ans1 , 后缀最值求出ans2即可

         时间复杂度 : O(NlogN)

[代码]

        

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int N = 3e5 + 10;
const ll inf = 1e18;

struct info
{
        int x , y;
} a[N];

int n;
int rk[N] , cnt[N] , sa[N] , height[N] , fa[N] , sz[N];
ll max1[N] , max2[N] , min1[N] , min2[N] , w[N] , ans1[N] , ans2[N];
char s[N];

template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); }
template <typename T> inline void chkmin(T &x,T y) { x = min(x,y); }
template <typename T> inline void read(T &x)
{
    T f = 1; x = 0;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
    x *= f;
}
inline void build_sa()
{
        static int x[N] , y[N] , cnt[N];
        for (int i = 1; i <= n; ++i) ++cnt[s[i]];
        for (int i = 1; i <= 256; ++i) cnt[i] += cnt[i - 1];
        for (int i = n; i >= 1; --i) sa[cnt[s[i]]--] = i;
        rk[sa[1]] = 1;
        for (int i = 2; i <= n; ++i) rk[sa[i]] = rk[sa[i - 1]] + (s[sa[i]] != s[sa[i - 1]]);
        for (int k = 1; rk[sa[n]] != n; k <<= 1)
        {
                for (int i = 1; i <= n; ++i)
                        x[i] = rk[i] , y[i] = (i + k <= n) ? rk[i + k] : 0;
                memset(cnt , 0 , sizeof(cnt));
                for (int i = 1; i <= n; ++i) ++cnt[y[i]];
                for (int i = 1; i <= n; ++i) cnt[i] += cnt[i - 1];
                for (int i = n; i >= 1; i--) rk[cnt[y[i]]--] = i;
                memset(cnt , 0 , sizeof(cnt));
                for (int i = 1; i <= n; ++i) ++cnt[x[i]];
                for (int i = 1; i <= n; ++i) cnt[i] += cnt[i - 1];
                for (int i = n; i >= 1; i--) sa[cnt[x[rk[i]]]--] = rk[i];
                rk[sa[1]] = 1;
                for (int i = 2; i <= n; ++i) rk[sa[i]] = rk[sa[i - 1]] + (x[sa[i]] != x[sa[i - 1]] || y[sa[i]] != y[sa[i - 1]]);
        }        
}
inline void get_height()
{
        int k = 0;
        for (int i = 1; i <= n; ++i)
        {
                int j = sa[rk[i] + 1];
                if (k) --k;
                while (s[i + k] == s[j + k]) ++k;
                height[rk[i]] = k;        
        }
}
inline bool cmp(info a , info b)
{
        return a.y > b.y;        
}
inline int get_root(int x)
{
        if (fa[x] == x) return x;
        else return fa[x] = get_root(fa[x]);
}
inline void merge(int x , int y)
{
        ll  A[5] = {0 , max1[x] , max2[x] , max1[y] , max2[y]} ,
                B[5] = {0 , min1[x] , min2[x] , min1[y] , min2[y]};
        if (sz[x] > sz[y]) swap(x , y);
        sz[y] += sz[x];
        fa[x] = y;
        sort(A + 1 , A + 5 , greater<ll>());
        sort(B + 1 , B + 5);
        max1[y] = A[1]; max2[y] = A[2];
        min1[y] = B[1]; min2[y] = B[2];
        return;
}

int main()
{
        
        scanf("%d" , &n);
        scanf("%s" , s + 1);
        for (int i = 1; i <= n; ++i) scanf("%lld" , &w[i]);
        build_sa();
        get_height();
        for (int i = 1; i < n; ++i)
        {
                a[i].x = i;
                a[i].y = height[i];        
        }
        for (int i = 1; i <= n; ++i)
        {
                fa[i] = i;
                sz[i] = 1;
                max1[i] = w[sa[i]] , max2[i] = -inf;
                min1[i] = w[sa[i]] , min2[i] = inf;
                ans2[i] = -inf;
        }
        ans2[0] = -inf;
         sort(a + 1 , a + n , cmp);
        for (int i = 1; i < n; ++i)
        {
                int rk = a[i].x , ht = a[i].y;
                int fx = get_root(rk) , fy = get_root(rk + 1);        
                ans1[0] += 1ll * sz[fx] * sz[fy]; 
                ans1[ht + 1] -= 1ll * sz[fx] * sz[fy];
                merge(fx , fy);
                fx = get_root(rk);
                chkmax(ans2[ht] , max(1ll * max1[fx] * max2[fx] , 1ll * min1[fx] * min2[fx]));
        }
        for (int i = 1; i < n; ++i) ans1[i] += ans1[i - 1];
        for (int i = n - 1; i >= 0; --i) ans2[i] = max(ans2[i + 1] , ans2[i]);
        for (int i = 0; i < n; ++i) 
                if (!ans1[i]) puts("0 0");
                else printf("%lld %lld\n" , ans1[i] , ans2[i]);
         
        return 0;
    
}

猜你喜欢

转载自www.cnblogs.com/evenbao/p/10549317.html