码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj 4199 品酒大会

时间:2020-01-31 14:18:17      阅读:49      评论:0      收藏:0      [点我收藏+]

标签:code   set   lin   scanf   sizeof   ons   前缀   后缀   最小值   

bzoj 4199 品酒大会

开始线段树合并学傻了直接拿线段树合并莽然后80pts滚粗

其实考虑,如果我们求出了 $ LCP(s_1,s_2) = i $ ,其中 $ s_1,s_2 $ 是后缀,的权值的和/最大值,做一遍后缀和/最大值就好了啊!

这个东西是可以 dp 的!由于 parent 树本质上是前缀树,parent 树上的 LCA 实际上就是 LCP 。

由于权值在前面不好处理,可以倒过来建前缀树,也就是建后缀树,这样权值就在最后一个位置啦

对于一个点来说它作为 LCP 的长度是确定了的($ len(x) $)所以可以直接对于每个点存一下最大值,最小值(因为有负数),siz 就好了。

其实这个必然可以用SA做的,没有用到后缀自动机的转移,其实完全可以直接建前缀树。只用后缀树的话,sa必然也可以的吧。

不算很难写吧?

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
#define MAXN 600006

int n , lst;
char ch[MAXN]; int w[MAXN];
ll ans = 0;

long long tot[MAXN] , res[MAXN];
struct SAM{
    int son[MAXN][27]; // sons
    int par[MAXN] , len[MAXN]; // node
    int head[MAXN] , nxt[MAXN<<1] , to[MAXN<<1]; // edge
    int cnt , ecnt;
    int sz[MAXN];
    void adde( int u , int v ) {
        to[++ecnt] = v;
        nxt[ecnt] = head[u];
        head[u] = ecnt;
    }
    int mx[MAXN] , semx[MAXN] , mn[MAXN] , semn[MAXN];
    void init(  ) {
        memset( son , 0 , sizeof son ) , memset( head , -1 , sizeof head );
        memset( mx , -0x3f , sizeof mx ) , memset( mn , 0x3f , sizeof mn );
        memset( semx , -0x3f , sizeof semx ) , memset( semn , 0x3f , sizeof semn );
        cnt = lst = 1; ecnt = 0;
    }
    void addall(  ) { // 将所有link边连到边集合中
        for( int i = 2 ; i <= cnt ; ++ i ) adde( par[i] , i );
    }
    void ins( int x , int w ) {
        int cur = ++ cnt;
        len[cur] = len[lst] + 1 , sz[cur] = 1;
        mx[cur] = mn[cur] = w;
        int p = lst;
        while( p && !son[p][x] ) son[p][x] = cur , p = par[p];
        if( !p ) par[cur] = 1;
        else {
            int q = son[p][x];
            if( len[q] == len[p] + 1 ) par[cur] = q;
            else {
                int cl = ++ cnt;
                memcpy( son[cl] , son[q] , sizeof son[q] );
                par[cl] = par[q]; // copy
                len[cl] = len[p] + 1 , par[q] = par[cur] = cl;
                for( ; son[p][x] == q ; p = par[p] ) son[p][x] = cl;
            }
        }
        lst = cur;
    }
    void dfs( int cur ) {
        int l = len[cur];
        for( int i = head[cur] ; ~i ; i = nxt[i] ) {
            int v = to[i];
            dfs( v );
            if( mx[v] > mx[cur] ) semx[cur] = mx[cur] , mx[cur] = mx[v];
            else if( mx[v] > semx[cur] ) semx[cur] = mx[v];
            if( semx[v] > semx[cur] ) semx[cur] = semx[v];

            if( mn[v] < mn[cur] ) semn[cur] = mn[cur] , mn[cur] = mn[v];
            else if( mn[v] < semn[cur] ) semn[cur] = mn[v];
            if( semn[v] < semn[cur] ) semn[cur] = semn[v];
            tot[l] += 1ll * sz[cur] * sz[v];
            sz[cur] += sz[v];
        }
        if( semx[cur] > -1e9 - 10 ) res[l] = max( res[l] , 1ll * mx[cur] * semx[cur] );
        if( semn[cur] < 1e9 + 10 ) res[l] = max( res[l] , 1ll * mn[cur] * semn[cur] );
//        printf("%d %d\n",len[cur] , sz[cur]);
    }
    void fuck() {
        puts("fuck");
    }
} S ;

int main() {
    S.init();
    cin >> n;
    scanf("%s",ch);
    for( int i = 0 ; i < n ; ++ i ) scanf("%d",&w[i]);
    for( int i = n - 1 ; i >= 0 ; -- i ) S.ins( ch[i] - 'a' , w[i] );
    for( int i = 0 ; i <= n ; ++ i ) res[i] = -1e18;
    S.addall( ) , S.dfs( 1 );
//    S.fuck();
    for( int i = n - 1 ; i >= 0 ; -- i ) tot[i] += tot[i + 1] , res[i] = max( res[i] , res[i + 1] );
    for( int i = 0 ; i < n ; ++ i )
        printf("%lld %lld\n",tot[i],res[i] == -1e18 ? 0 : res[i]);
}

bzoj 4199 品酒大会

标签:code   set   lin   scanf   sizeof   ons   前缀   后缀   最小值   

原文地址:https://www.cnblogs.com/yijan/p/bzoj4199.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!