其实是一道挺简单的题目
但是自己之前没怎么用过failfailfail树所以不会还是菜
对所有的串建出ACACAC自动机,建出failfailfail树
failfailfail树就是由fail[i]−>ifail[i]->ifail[i]−>i的边组成的树,与ACACAC自动机上节点一一对应
然后一个简单O(n∑len[i])DPO(n\sum len[i])DPO(n∑len[i])DP,设f[i]f[i]f[i]为选择iii为最后一个串时的最大答案
那么f[i]=max(f[j])f[i]=max(f[j])f[i]=max(f[j]) (j(j(j是iii的子串)))
建出failfailfail树后,对于每个字符串的前缀单点查询,最后加上自己的权值后修改这个串子树的maxmaxmax值,用线段树即可维护
就可以在O((∑len[i]) log∑len[i])O((\sum len[i])\,log\sum len[i])O((∑len[i])log∑len[i])时间内完成
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define N 1000005
#define ll long long
#include <vector>
using namespace std;
int trie[N][26],dfn[N],fail[N];
int f[N][3],q[N],size[N],cun[N],ys[N],len[N];
struct node{
ll max,flag;
}tree[N<<1];
ll dp[N],val[N],s;
int i,j,k,m,n,o,p,l,t,tot,now,sum;
char c[N];
vector<int> a[N];
queue<int> h;
void build(int n)
{
int now=0;
for (int i=1;i<=n;i++)
{
if (!trie[now][c[i]-'a'])
trie[now][c[i]-'a']=++sum;
now=trie[now][c[i]-'a'],cun[++cun[0]]=now;
}
}
void insert(int x,int y) {f[++t][1]=y,f[t][2]=q[x],q[x]=t;}
void work()
{
int now=0;
for (i=0;i<=25;i++)
if (trie[now][i]) h.push(trie[now][i]);
while (!h.empty())
{
int st=h.front();h.pop();
for (int i=0;i<=25;i++)
if (trie[st][i]) fail[trie[st][i]]=trie[fail[st]][i],h.push(trie[st][i]);
else trie[st][i]=trie[fail[st]][i];
}
for (i=1;i<=sum;i++) insert(fail[i]+1,i+1);//printf("%d %d\n",fail[i],i);
}
void dg(int t)
{
dfn[t]=++tot,size[t]=1;
for (int k=q[t];k;k=f[k][2])
{
dg(f[k][1]);
size[t]+=size[f[k][1]];
}
}
ll max(ll a,ll b) {return a>b?a:b;}
void downdata(ll x)
{
if (!tree[x].flag) return;
ll k=tree[x].flag;
tree[x<<1].max=max(tree[x<<1].max,k),tree[x<<1|1].max=max(tree[x<<1|1].max,k);
tree[x<<1].flag=max(tree[x<<1].flag,k),tree[x<<1|1].flag=max(tree[x<<1|1].flag,k);
tree[x].flag=0;
}
void change(ll x,ll l,ll r,ll st,ll en,ll ad)
{
if (l>=st&&r<=en) tree[x].max=max(tree[x].max,ad),tree[x].flag=max(tree[x].flag,ad);
else {
downdata(x);
ll mid=(l+r)/2;
if (en<=mid) change(x<<1,l,mid,st,en,ad);
else if (st>mid) change(x<<1|1,mid+1,r,st,en,ad);
else change(x<<1,l,mid,st,mid,ad),change(x<<1|1,mid+1,r,mid+1,en,ad);
tree[x].max=max(tree[x<<1].max,tree[x<<1|1].max);
}
}
ll query(ll x,ll l,ll r,ll pos)
{
if (l==r) return tree[x].max;
else {
downdata(x);
ll mid=(l+r)/2;
if (pos<=mid) return query(x<<1,l,mid,pos);
else return query(x<<1|1,mid+1,r,pos);
}
}
int main()
{
freopen("word.in","r",stdin);
freopen("word.out","w",stdout);
scanf("%d\n",&n);
for (i=1;i<=n;i++)
{
scanf("%s %lld\n",c+1,&val[i]);
cun[0]=0;len[i]=strlen(c+1);
build(len[i]);
for (j=1;j<=cun[0];j++) a[i].push_back(cun[j]);
}
work();
dg(1);
ll ans=0;
for (i=1;i<=n;i++)
{
dp[i]=0;
for (j=0;j<=len[i]-1;j++)
dp[i]=max(dp[i],query(1,1,tot,dfn[a[i][j]+1]));
dp[i]+=val[i];
change(1,1,tot,dfn[a[i][len[i]-1]+1],dfn[a[i][len[i]-1]+1]+size[a[i][len[i]-1]+1]-1,dp[i]);
}
for (i=1;i<=n;i++) ans=(dp[i]>ans?dp[i]:ans);
printf("%lld\n",ans);
return 0;
}