HDU-5470 Typewriter (后缀自动机优化dp)

HDU-5470 Typewriter (后缀自动机优化dp)

这个\(dp\)有两种转移

1.\(dp[i-1]+cost[s[i]] \rightarrow dp[i]\)

2.$ dp[j]+(i-j) \cdot A+2 \cdot B \rightarrow dp[i] $ ( $s_{j+1,i} $ 是 \(s_{1,j}\) 的一个子串)

第一种转移不必多说

对于第二种转移,合法的下标\(j\)一定是到\(i-1\)的一段连续区间

设临界的下标\(j\)\(x\),找到\(S_{x,i}\)对应的状态\(st\),那么\(min\{endpos_{st}\} \leq x\)

我们在转移的同时,不断匹配\(S_{1,i}\)对应的状态\(p\),那么\(S_{x,i}\)对应的状态就一定是\(p\)\(parent/link\)树上的一个祖先

暴力一点的话,可以通过 树上倍增+线段树/树状数组 来实现

#include<bits/stdc++.h>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)

#define pb push_back
template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); }

char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}

const int N=2e5+10;

int n,A,B;
char S[N];
int Cost[30];

ll dp[N],temp[N];
int fa[20][N];
int vis[N];
struct I_Hate_It{// 线段树
    ll s[N<<1];
    int bit;
    void Init(){
        bit=1;
        while(bit<=n+2) bit<<=1;
        rep(i,1,bit+n+1) s[i]=1e18;
    }
    void Upd(int p,ll x){
        p++;
        p+=bit,s[p]=x;
        while(p>1) p>>=1,s[p]=min(s[p<<1],s[p<<1|1]);
    }
    ll Que(int l,int r) {
        l++,r++;
        if(l==r) return s[l+bit];
        ll res=1e18;
        for(l+=bit-1,r+=bit+1;l^r^1;l>>=1,r>>=1) {
            if(~l&1) cmin(res,s[l^1]);
            if(r&1) cmin(res,s[r^1]);
        }
        return res;
    }
}Tree;


int trans[N][26],link[N],len[N],stcnt,lst,First[N];

void Init(){
    link[0]=-1,len[0]=0;
    rep(i,0,stcnt) {
        vis[i]=0;
        rep(j,0,25) trans[i][j]=0;
    }
    lst=stcnt=0;
}

void Extend(int c) {
    int cur=++stcnt,p=lst;
    First[cur]=len[cur]=len[p]+1;
    while(~p && !trans[p][c]) trans[p][c]=cur,p=link[p];
    if(p==-1) link[cur]=0;
    else {
        int q=trans[p][c];
        if(len[q]==len[p]+1) link[cur]=q;
        else {
            int clone=++stcnt;
            memcpy(trans[clone],trans[q],104);
            First[clone]=First[q];
            len[clone]=len[p]+1,link[clone]=link[q];
            while(~p && trans[p][c]==q) trans[p][c]=clone,p=link[p];
            link[cur]=link[q]=clone;
        }
    }
    lst=cur;
}

int main(){
    rep(kase,1,rd()) {
        scanf("%s",S+1),n=strlen(S+1);
        dp[0]=0;
        Init(),Tree.Init();
        rep(i,1,n) Extend(S[i]-'a');
        rep(i,0,25) Cost[i]=rd();
        A=rd(),B=rd();
        int p=0;
        fa[0][0]=0; rep(i,1,stcnt) fa[0][i]=link[i];
        rep(i,1,18) rep(j,1,stcnt) fa[i][j]=fa[i-1][fa[i-1][j]];
        rep(i,1,n) {
            dp[i]=1e18;
            p=trans[p][S[i]-'a'];
            int now=p;
            if(now && First[now]<i-len[link[now]]) cmin(dp[i],Tree.Que(First[now],i-1));
            else {
                drep(j,17,0) if(fa[j][now] && First[fa[j][now]]>=i-len[link[fa[j][now]]])
                    now=fa[j][now];
                now=fa[0][now]; 
                if(now && max(i-len[now],First[now])<i-len[link[now]]) 
                    cmin(dp[i],Tree.Que(max(i-len[now],First[now]),i-1));//倍增找到合法子串
            }
            dp[i]+=1ll*i*A+B*2;
            cmin(dp[i],dp[i-1]+Cost[S[i]-'a']);
            Tree.Upd(i,dp[i]-1ll*i*A);
        }
        printf("Case #%d: %lld\n",kase,dp[n]);
    }
}



posted @ 2020-01-20 10:19  chasedeath  阅读(182)  评论(0编辑  收藏  举报