[BZOJ4199][Noi2015]品酒大会

4199: [Noi2015]品酒大会

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 1157  Solved: 655
[Submit][Status][Discuss]

Description

 

一年一度的“幻影阁夏日品酒大会”隆重开幕了。大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加。

在大会的晚餐上,调酒师 Rainbow 调制了 nn 杯鸡尾酒。这 nn 杯鸡尾酒排成一行,其中第 ii 杯酒 (1in1≤i≤n ) 被贴上了一个标签 sisi ,每个标签都是 2626 个小写英文字母之一。设 Str(l,r)Str(l,r) 表示第 ll 杯酒到第 rr 杯酒的 rl+1r−l+1 个标签顺次连接构成的字符串。若 Str(p,po)=Str(q,qo)Str(p,po)=Str(q,qo) ,其中 1ppon1≤p≤po≤n ,1qqon1≤q≤qo≤n ,pqp≠q ,pop+1=qoq+1=rpo−p+1=qo−q+1=r ,则称第 pp 杯酒与第 qq 杯酒是“rr 相似” 的。当然两杯“rr 相似” (r>1r>1 )的酒同时也是“11 相似”、“22 相似”、… 、“(r1)(r−1) 相似”的。特别地,对于任意的 1p,qn1≤p,q≤n ,pqp≠q ,第 pp 杯酒和第 qq 杯酒都是“00 相似”的。

在品尝环节上,品酒师 Freda 轻松地评定了每一杯酒的美味度,凭借其专业的水准和经验成功夺取了“首席品酒家”的称号,其中第 ii 杯酒 (1in1≤i≤n ) 的美味度为 aiai 。现在 Rainbow 公布了挑战环节的问题:本次大会调制的鸡尾酒有一个特点,如果把第 pp 杯酒与第 qq 杯酒调兑在一起,将得到一杯美味度为 apaqapaq 的酒。现在请各位品酒师分别对于 r=0,1,2,,n1r=0,1,2,…,n−1 ,统计出有多少种方法可以选出 22 杯“rr 相似”的酒,并回答选择 22 杯“rr 相似”的酒调兑可以得到的美味度的最大值。

Input

 

输入文件的第 11 行包含 11 个正整数 nn ,表示鸡尾酒的杯数。

22 行包含一个长度为 nn 的字符串 SS ,其中第 ii 个字符表示第 ii 杯酒的标签。

33 行包含 nn 个整数,相邻整数之间用单个空格隔开,其中第 ii 个整数表示第 ii 杯酒的美味度 aiai 。

Output

 输出文件包括 nn 行。第 ii 行输出 22 个整数,中间用单个空格隔开。第 11 个整数表示选出两杯“(i1)(i−1) 相似”的酒的方案数,第 22 个整数表示选出两杯“(i1)(i−1) 相似”的酒调兑可以得到的最大美味度。若不存在两杯“(i1)(i−1) 相似”的酒,这两个数均为 00 。

Sample Input

 10
ponoiiipoi
2 1 4 7 4 8 3 6 4 7

Sample Output

45 56
10 56
3 32
0 0
0 0
0 0
0 0
0 0
0 0
0 0

 

先处理出$height$数组,然后看到$height$大的对小的没有影响,所以按照$height$从大到小排序

然后每次并查集合并两个后缀,记录下里面最大最小值以及后缀数量即可

#include <cstdio>
#include <algorithm>
using namespace std; 
typedef long long ll;
const ll INF = 1LL << 62;
const int maxn = 300000 + 10;
int n, m;
int sa[maxn], rank[maxn], height[maxn], tp[maxn], tax[maxn];
inline bool cmp(const int *arr, const int &x, const int &y,const int &l){
    return arr[x] == arr[y] && arr[x + l] == arr[y + l];
}
inline void Rsort(){
    for(int i = 0; i <= m; i++) tax[i] = 0;
    for(int i = 1; i <= n; i++) tax[rank[tp[i]]]++;
    for(int i = 1; i <= m; i++) tax[i] += tax[i - 1];
    for(int i = n; i; i--) sa[tax[rank[tp[i]]]--] = tp[i];
}
char s[maxn];
void suffix(){
    m = 256;
    for(int i = 1; i <= n; i++){
        tp[i] = i;
        rank[i] = s[i];
    }
    Rsort();
    for(int p, w = 1; w < n; m = p, w <<= 1){
        p = 0;
        for(int i = n - w + 1; i <= n; i++) tp[++p] = i;
        for(int i = 1; i <= n; i++) if(sa[i] > w) tp[++p] = sa[i] - w;
        Rsort();
        swap(rank, tp);
        rank[sa[1]] = p = 1;
        for(int i = 2; i <= n; i++)
            rank[sa[i]] = cmp(tp, sa[i - 1], sa[i], w) ? p : ++p;
        if(p == n) break;
    }
    for(int i = 1, j, k = 0; i <= n; height[rank[i++]] = k)
        for(k ? k-- : k, j = sa[rank[i] - 1]; s[i + k] == s[j + k]; k++);
};
ll cnt[maxn], ans[maxn];
int fa[maxn], siz[maxn], mi[maxn], mx[maxn];
int Find(int x){
    return x == fa[x] ? x : fa[x] = Find(fa[x]);
}
inline void Union(int x, int y, int val){
    x = Find(x);
    y = Find(y);
    if(x == y) return;
    cnt[val] += (ll)siz[x] * siz[y];
    ans[val] = max(ans[val], max((ll)mi[x] * mi[y], (ll)mx[x] * mx[y]));
    fa[y] = x;
    siz[x] += siz[y];
    mx[x] = max(mx[x], mx[y]);
    mi[x] = min(mi[x], mi[y]);
}
int num[maxn];
struct Node{
    int pos, val;
    Node(){}
    Node(int _p, int _v): pos(_p), val(_v){}
    bool operator < (const Node &rhs) const {
        return val > rhs.val;
    }
}no[maxn];
int main(){
    scanf("%d%s", &n, s + 1);
    for(int i = 1; i <= n; i++) scanf("%d", num + i);
    suffix();
    for(int i = 1; i <= n; i++){
        cnt[i] = 0;
        ans[i] = -INF;
    }
    for(int i = 1; i <= n; i++){
        fa[i] = i;
        siz[i] = 1;
        mi[i] = mx[i] = num[i];
    }
    for(int i = 1; i < n; i++)
        no[i] = Node(i, height[i + 1]);
    sort(no + 1, no + n);
    for(int i = 1; i < n; i++) Union(sa[no[i].pos], sa[no[i].pos + 1], no[i].val);
    for(int i = n - 1; ~i; i--){
        cnt[i] += cnt[i + 1];
        ans[i] = max(ans[i], ans[i + 1]);
    }
    for(int i = 0; i < n; i++)
        printf("%lld %lld\n", cnt[i], cnt[i] ? ans[i] : 0);
    return 0;
}

 Update:

可以直接建后缀树,然后树形dp即可

建出反串的SAM,它的Parent树即为原串的后缀树

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
inline int readint(){
    int f = 1, n = 0;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(ch <= '9' && ch >= '0'){
        n = (n << 1) + (n << 3) + ch - '0';
        ch = getchar();
    }
    return f * n;
}
typedef long long ll;
const int maxn = 300000 + 10;
const ll INF = 1LL << 62;
struct State{
    int son[26], link, len, val, siz;
    State(){}
}st[maxn * 2];
int last, sam_cnt;
void sam_init(){
    last = sam_cnt = 0;
    st[0].link = -1;
    st[0].len = 0;
    memset(st[0].son, 0, sizeof(st[0].son));
    st[0].siz = 0;
}
inline void sam_extend(char c, int val){
    int cur = ++sam_cnt, idx = c - 'a';
    st[cur].len = st[last].len + 1;
    st[cur].siz = 1;
    memset(st[cur].son, 0, sizeof(st[cur].son));
    int p;
    for(p = last; p != -1 && !st[p].son[idx]; p = st[p].link) st[p].son[idx] = cur;
    if(p == -1) st[cur].link = 0;
    else{
        int q = st[p].son[idx];
        if(st[p].len + 1 == st[q].len) st[cur].link = q;
        else{
            int clone = ++sam_cnt;
            st[clone].len = st[p].len + 1;
            memcpy(st[clone].son, st[q].son, sizeof(st[q].son));
            st[clone].link = st[q].link;
            st[clone].siz = 0;
            for(; p != -1 && st[p].son[idx] == q; p = st[p].link) st[p].son[idx] = clone;
            st[q].link = st[cur].link = clone;
        }
    }
    st[cur].val = val;
    last = cur;
}
struct Edge{
    int to, next;
    Edge(){}
    Edge(int _t, int _n): to(_t), next(_n){}
}e[maxn * 2];
int fir[maxn * 2] = {0}, e_cnt = 0;
inline void add(int u, int v){
    e[++e_cnt] = Edge(v, fir[u]); fir[u] = e_cnt;
}
ll cnt[maxn], ans[maxn];
ll mx[maxn * 2], mi[maxn * 2];
void dfs(int u){
    if(st[u].siz == 1) mx[u] = mi[u] = st[u].val;
    else{
        mx[u] = -INF;
        mi[u] = INF;
    }
    for(int v, i = fir[u]; i; i = e[i].next){
        v = e[i].to;
        dfs(v);
        if(mx[u]!= -INF && mi[u] != INF && st[v].siz)
            ans[st[u].len] = max(ans[st[u].len], max(mx[u] * mx[v], mi[u] * mi[v]));    
        cnt[st[u].len] += (ll)st[u].siz * st[v].siz;
        mx[u] = max(mx[u], mx[v]);
        mi[u] = min(mi[u], mi[v]);
        st[u].siz += st[v].siz;
    }
}
int n;
char s[maxn];
int v[maxn];
int main(){
    n = readint();
    scanf("%s", s + 1);
    for(int i = 1; i <= n; i++)    v[i] = readint();
    sam_init();
    for(int i = n; i; i--) sam_extend(s[i], v[i]);
    for(int i = 1; i <= sam_cnt; i++)
        add(st[i].link, i);
    for(int i = 0; i < n; i++){
        cnt[i] = 0;
        ans[i] = -INF;
    }
    dfs(0);
    for(int i = n - 2; ~i; i--){
        ans[i] = max(ans[i], ans[i + 1]);
        cnt[i] += cnt[i + 1];
    }
    for(int i = 0; i < n; i++)
        printf("%lld %lld\n", cnt[i], cnt[i] ? ans[i] : 0);
    return 0;
}

 

posted @ 2017-09-28 21:33  Elder_Giang  阅读(164)  评论(0编辑  收藏  举报