• [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
    " , ans1[i] , ans2[i]);
             
            return 0;
        
    }
  • 相关阅读:
    离线安装SharePoint 2010必备组件下载链接
    skrollr——兼容性超强的视差滚动js插件
    Heroku实战入门(二)简单实战
    xp中安装sybase15.7遇到的三个问题
    思科三层交换机开启路由模式的方法
    Heroku实战入门(一)初识heroku
    Heroku实战入门(三)常用命令
    Splinter——开源的轻量级前端测试工具
    Request.ServerVariables 转
    Page.MaintainScrollPositionOnPostBack 属性
  • 原文地址:https://www.cnblogs.com/evenbao/p/10549317.html
Copyright © 2020-2023  润新知