[BZOJ4199][Noi2015]品酒大会

4199: [Noi2015]品酒大会

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

Description

 

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

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

在品尝环节上,品酒师 Freda 轻松地评定了每一杯酒的美味度,凭借其专业的水准和经验成功夺取了“首席品酒家”的称号,其中第 ii 杯酒 (1≤i≤n1≤i≤n ) 的美味度为 aiai 。现在 Rainbow 公布了挑战环节的问题:本次大会调制的鸡尾酒有一个特点,如果把第 pp 杯酒与第 qq 杯酒调兑在一起,将得到一杯美味度为 apaqapaq 的酒。现在请各位品酒师分别对于 r=0,1,2,…,n−1r=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 个整数表示选出两杯“(i−1)(i−1) 相似”的酒的方案数,第 22 个整数表示选出两杯“(i−1)(i−1) 相似”的酒调兑可以得到的最大美味度。若不存在两杯“(i−1)(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  jzyy  阅读(189)  评论(0)    收藏  举报