這個\(dp\)有兩種轉移c++
1.\(dp[i-1]+cost[s[i]] \rightarrow dp[i]\)git
2.$dp[j]+(i-j)\cdot A+2\cdot B \rightarrow dp[i] \((\)s_{j+1,i}\(是\)s_{1,j}$的一個子串)數組
第一種轉移沒必要多說優化
對於第二種轉移,合法的下標\(j\)必定是到\(i-1\)的一段連續區間spa
設臨界的下標\(j\)爲\(x\),找到\(S_{x,i}\)對應的狀態\(st\),那麼\(min\{endpos_{st}\} \leq x\)code
咱們在轉移的同時,不斷委會\(S_{1,i}\)對應的狀態\(p\),那麼\(S_{x,i}\)對應的狀態就必定是\(p\)在\(parent/link\)樹上的一個祖先get
暴力一點的話,能夠經過 樹上倍增+線段樹/樹狀數組 來實現it
#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]); } }