zoukankan      html  css  js  c++  java
  • luogu解题报告:P2178[NOI2015]品酒大会

    题意

    给定一个字符串S和一个权值函数a(i),求对于0r<n,长度为r的相等子串对个数和相等子串对开头两位置权值乘积的最大值。

    My Idea

    首先看到标签知道是后缀数组,那么就先搞出height数组。

    考虑一个naive的方法:从大到小枚举r,每次只考虑两位置最大为r匹配(而不考虑5匹配也能是3匹配的)。我们建一棵height的线段树。对于每一次,我们先找出height中的每一个height[p] = r的位置,然后向前后二分并用线段树判断,找到最大的使height[p]为最小值的区间,然后左右乘一乘就算出来了。

    然而复杂度是O(nlg2n)的,300000的时候显然会超时(当然松爷肯定能卡过去)。

    正解

    居然是并查集……
    最后一步变成边计算边合并,维护一个带权并查集就好了。神思路……
    如果用DA就是O(nlgn+nα(n)),如果用DC3就是O(nα(n))了(不过蒟蒻显然不会)..

    Code

    
    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn = 300005;
    struct S_A {
        int A[maxn], SA[maxn], C[maxn], rank[maxn], h[maxn], n;
        struct p {
            int x[2], id;
            p(){}
            p(int a, int b, int c):id(c) {x[0] = a, x[1] = b;}
        } RE[maxn], RT[maxn];
        S_A():n(0){}
        void radix_sort() {
            for (int y = 1; y >= 0; y--) {
                memset(C, 0, sizeof C);
                register int i;
                for (i = 1; i <= n; i++) C[RE[i].x[y]]++;
                for (i = 1; i < maxn; i++) C[i] += C[i-1];
                for (i = n; i >= 1; i--) RT[C[RE[i].x[y]]--] = RE[i];
                for (i = 1; i <= n; i++) RE[i] = RT[i];
            }
            for (int i = 1; i <= n; i++) {
                rank[RE[i].id] = rank[RE[i-1].id];
                if (RE[i].x[1] != RE[i-1].x[1] || RE[i].x[0] != RE[i-1].x[0]) rank[RE[i].id]++;
            }
        }
        void build_sa() {
            for (int i = 1; i <= n; i++) RE[i] = p(A[i], 0, i);
            radix_sort();
            for (int k = 1; k < n; k <<= 1) {
                for (int i = 1; i <= n; i++) RE[i] = p(rank[i], i+k<=n?rank[i+k]:0, i);
                radix_sort();
            }
            for (int i = 1; i <= n; i++) SA[rank[i]] = i;
        }
        void build_height() {
            for (int ht = 0, i = 1; i <= n; i++) {
                if (rank[i] == 1) ht = 0;
                else {
                    int k = SA[rank[i]-1];
                    if (--ht < 0) ht = 0;
                    while (A[i+ht] == A[k+ht]) ht++;
                }
                h[rank[i]] = ht;
            }
        }
            void init(const char *str) {
            for (; *str != ''; ++str) A[++n] = *str-'a'+1;
            build_sa();
            build_height();
        }
    }SA;
    char str[maxn];
    long long a[maxn];
    long long ansx[maxn], ansy[maxn];
    
    struct p {
        int to, next;
    } edge[maxn];
    int head[maxn], top = 0;
    void push(int i, int j) {
        edge[++top].to = j;
        edge[top].next = head[i];
        head[i] = top;
    }
    
    int fa[maxn];
    long long siz[maxn], mx[maxn], mn[maxn];
    long long cnt = 0, maxd = LONG_LONG_MIN;
    int findp(int i)
    { return fa[i]?fa[i]=findp(fa[i]):i; }
    void link(int i, int j) {
        //cout << i << " " << j << endl;
        if (!i || !j) return;
        int p = findp(i), q = findp(j);
        if (p != q) {
            maxd = max(maxd, max(mn[p]*mn[q], max(mx[p]*mx[q], max(mn[p]*mx[q], mx[p]*mn[q]))));
            cnt += siz[p]*siz[q];
            fa[p] = q;
            siz[q] += siz[p];
            mx[q] = max(mx[q], mx[p]);
                    mn[q] = min(mn[q], mn[p]);
        }
    }
    int main() {
        int n; scanf("%d", &n);
        scanf("%s", str);
        for (int i = 1; i <= n; i++) scanf("%lld", &a[i]), siz[i] = 1, fa[i] = 0;
        SA.init(str);
        for (int i = 1; i <= n; i++) mn[i] = mx[i] = a[SA.SA[i]];
        for (int i = 1; i <= n; i++) push(SA.h[i], i);
        for (int i = n-1; i >= 0; i--) {
                for (int j = head[i]; j; j = edge[j].next) {
                int to = edge[j].to, p = findp(to), q = findp(to-1);
                maxd = max(maxd, max(mn[p]*mn[q], max(mx[p]*mx[q], max(mn[p]*mx[q], mx[p]*mn[q]))));
            }
            for (int j = head[i]; j; j = edge[j].next) {
                int to = edge[j].to;
                link(to-1, to);
            }
            ansx[i] = cnt;
            ansy[i] = maxd;
            if (ansy[i] == LONG_LONG_MIN) ansy[i] = 0;
        }
        for (int i = 0; i < n; i++)
            printf("%lld %lld
    ", ansx[i], ansy[i]);
        return 0;
    }
  • 相关阅读:
    JavaScript高度和宽度详解
    VC6程序图标
    VC++中的Dlg,App,Doc,view
    Vista桌面图标无法拖动
    VC2008中IE8脚本错误问题解决
    单文件安装包制作(转)
    AutoResetEvent与ManualResetEvent区别
    纯JavaScript中调用WebServices
    动态加载程序集Assembly.Load
    VC++小知识积累
  • 原文地址:https://www.cnblogs.com/ljt12138/p/6684343.html
Copyright © 2011-2022 走看看