【NOIP2021国庆集训Day1】A.撰写博客

【题意】

给定一个长度为n的文章(小写字母),和m个不合法单词,修改文章中每个字母都有$a_i$的代价,问要文章中不存在不合法的单词,最小代价是多少

数据范围:$n\leq2*10^5,m\leq10$

【分析】

首先,我们可以预处理出来每个位置作为结尾,不包含任何不合法单词的最长的区间pos[i](也就是左端点最远的位置)

然后,我们考虑dp

对于位置i,能够由j位置转移而来,当且仅当j+1-i之间没有不合法单词,所以我们要在pos[i]之后转移而来

$f[i]=min_{pos[i]\leq j \le i}{f[j]}+a[i],$

这个式子可以用单调队列来优化转移

这样时间复杂度为$O(nm+\sum{|len|})$

【代码】

 

#include<bits/stdc++.h>
using namespace std;
#define re register
#define int long long
inline int read()
{
    int f=1,lzx=0;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-f;c=getchar();}
    while(c<='9'&&c>='0'){lzx=lzx*10+c-'0';c=getchar();}
    return lzx*f;
}
const int N=2e5+10,p=1e6+3;
char s[N],t[12][N];
int mod[2]={1e9+7,1e9+9},a[N],len[12],q[N],h,T,f[N],
    hash1[N][2],hasht[12][2],power[N][2],fir[N];
inline int calc(int x,int l,int id)
{
    int y=x+l-1,res=hash1[y][id];
    res-=hash1[x-1][id]*power[y-x+1][id]%mod[id];
    return (res+mod[id])%mod[id];
}
inline int check(int x,int y)
{
    return calc(x,len[y],0)==hasht[y][0]&&calc(x,len[y],1)==hasht[y][1];
}
inline int fit(int x)
{
    if(fir[x]==x)return fir[x-1];
    return fir[x];
}
signed main()
{
    freopen("wzadx.in","r",stdin);
    freopen("wzadx.out","w",stdout);
    int n=read(),m=read(),ans=0;
    scanf("%s",s+1);
    for(re int i=1;i<=n;i++)
        a[i]=read(),ans+=a[i];
    for(re int i=1;i<=m;i++)
    {
        scanf("%s",t[i]+1);
        len[i]=strlen(t[i]+1);
    }
    power[0][0]=power[0][1]=1;
    for(re int i=1;i<=n;i++)
        for(re int j=0;j<2;j++)
            power[i][j]=power[i-1][j]*p%mod[j];
    for(re int i=1;i<=n;i++)
        for(re int j=0;j<2;j++)
            hash1[i][j]=(hash1[i-1][j]*p+s[i]-'a')%mod[j];
    for(re int i=1;i<=m;i++)
    {
        int tmp[2];tmp[0]=tmp[1]=0;
        for(re int j=1;j<=len[i];j++)
            for(re int k=0;k<2;k++)
                tmp[k]=(tmp[k]*p+t[i][j]-'a')%mod[k];
        hasht[i][1]=tmp[1];
        hasht[i][0]=tmp[0];
    }
    for(re int i=1;i<=n;i++)
        for(re int j=1;j<=m;j++)
        {
            if(i+len[j]-1>n+1)continue;
            if(check(i,j))
                fir[i+len[j]-1]=max(fir[i+len[j]-1],i);
        }
    for(re int i=1;i<=n+1;i++)
        fir[i]=max(fir[i],fir[i-1]);
    q[++h]=0;
    for(re int i=1;i<=n+1;i++)
    {
        while(T<h&&q[T+1]<fir[i-1])T++;
        f[i]=f[q[T+1]]+a[i];
        while(T<h&&f[q[h]]>=f[i])h--;
        q[++h]=i;
    }
    cout<<f[n+1]<<endl;
    return 0;
}

 

posted @ 2021-10-01 20:48  andyc_03  阅读(47)  评论(0编辑  收藏  举报