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 != '\0'; ++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\n", ansx[i], ansy[i]);
    return 0;
}
posted @ 2017-02-19 20:30  ljt12138  阅读(177)  评论(0编辑  收藏  举报