小 正在研究魔法。
小 得到了遠古時期的魔法咒語 ,這個咒語共有 個音節,每個音節都可以
抽象爲一個小寫英文字母。
但是很快小 發現這個咒語並不能直接說出——它具有一定的危險性。
小 進行了一些仔細的研究,很快發現危險來源於 個禁忌詞 。
小 發現,只要說出的咒語中,連續地包含了其中某個禁忌詞,那麼就會帶來很
大的危險。換言之,對於任意 , 都不能是最終說出的咒語 的子串。
於是小 決定在原來的咒語 上做出一定的刪減,使得它不再包含任何禁忌詞。
小 發現如果他跳過咒語中第 個音節,那麼咒語的威力會減少 。
小 想要知道,如何跳過音節可以得到一個安全的咒語,而威力的減少量最少。
值得一提的是,如果小 跳過了某個音節,那麼與之相鄰兩個音節也不會變得連續。
但是小 並不會,請你幫幫他。
先用所有的對做,得到若干個區間
那麼我們的問題就轉化成了有個點和若干個區間,每個點有點權,你要選若干個點使得每個區間內都至少包含一個點,問最小點權和是多少
考慮
先將區間按照升序排序
設表示使前個區間都合法,最後一個點爲的最小點權和是多少
那麼對一個區間,其有效的的區間也爲
考慮肯定是由轉移來的
於是我們可以去掉一維
設表示最後一個點爲時的答案
然後考慮一個一個的加區間
如圖,
後一個區間的藍色部分的值與前一個區間應是一樣的
而紅色區間的值則由綠色區間的值轉移過來,設紅色區間的一個值爲,則有
我們可以用線段樹查詢綠色區間的最小值,並維護紅色區間的增值
/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年11月11日 星期一 08時24分38秒
*******************************/
#include <cstdio>
#include <fstream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 200005;
const int maxm = 2000006;
const int inf = 0x7f7f7f7f;
//cin 省掉了快讀
int n,m,tot;
int nxt[maxn],l[maxm],r[maxm],w[maxn],id[maxn];
char s[maxn],t[maxn];
bool cmp (int x,int y){ return r[x]^r[y]?r[x]<r[y]:l[x]<l[y];}
//{{{Get
void Get (char *s,int len)
{
int j=1,k=0;
reset(nxt);
while (j<=len){
if (!k||s[j]==s[k]) nxt[++j]=++k;
else k=nxt[k];
}
}
//}}}
//{{{Match
int Match (char *s,char *t)//s appears in t
{
int len=strlen(s+1);
Get(s,len);
int j=1,k=1,ans=0;
while (k<=n){
if (!j||s[j]==t[k]) ++j,++k;
else j=nxt[j];
if (j==len+1){
l[++tot]=k-len,r[tot]=k-1,id[tot]=tot;
j=nxt[j];
}
}
return ans;
}
//}}}
//{{{SegmentTree
namespace SegmentTree
{
#define cl k<<1
#define cr k<<1|1
#define lm (lt[k]+rt[k])/2
#define rm (lt[k]+rt[k])/2+1
const int maxt = 1000006;
int lt[maxt],rt[maxt],val[maxt],lazy[maxt],tag[maxt],sum[maxt];
//{{{build
void build (int l,int r,int k=1)
{
lt[k]=l,rt[k]=r,val[k]=inf;
if (l==r) return void(sum[k]=w[l]);
build(l,lm,cl);
build(rm,r,cr);
sum[k]=min(sum[cl],sum[cr]);
}
//}}}
//{{{pushdownl
void pushdownl (int k)
{
val[cl]=lazy[k]*sum[cl],val[cr]=lazy[k]*sum[cr];
lazy[cl]+=lazy[k],lazy[cr]+=lazy[k];
lazy[k]=0;
}
//}}}
//{{{pushdownt
void pushdownt (int k)
{
tag[cl]+=tag[k],tag[cr]+=tag[k];
val[cl]+=tag[k],val[cr]+=tag[k];
tag[k]=0;
}
//}}}
//{{{modify
void modify (int l,int r,int v,int k=1)
{
if (lt[k]>=l&&rt[k]<=r){
val[k]=sum[k]+v;
++lazy[k],tag[k]+=v;
return;
}
if (lazy[k]) pushdownl(k);
if (tag[k]) pushdownt(k);
if (l<=lm) modify(l,r,v,cl);
if (r>=rm) modify(l,r,v,cr);
val[k]=min(val[cl],val[cr]);
}
//}}}
//{{{query
int query (int l,int r,int k=1)
{
if (lt[k]>=l&&rt[k]<=r) return val[k];
int res=inf;
if (lazy[k]) pushdownl(k);
if (tag[k]) pushdownt(k);
if (l<=lm) res=min(res,query(l,r,cl));
if (r>=rm) res=min(res,query(l,r,cr));
return res;
}
//}}}
}
using namespace SegmentTree;
//}}}
int main()
{
scanf("%d%d",&n,&m);
scanf("%s",s+1);
for (int i=1;i<=n;++i) cin>>w[i];
for (int i=1;i<=m;++i){
scanf("%s",t+1);
Match(t,s);
}
sort(id+1,id+tot+1,cmp);
int cnt=0;
for (int i=1;i<=tot;++i){
int cur=id[i];
while (i+1<=tot&&l[id[i+1]]==l[cur]&&r[id[i+1]]==r[cur]) ++i;
id[++cnt]=cur;
}
tot=cnt;
if (!tot) return printf("0\n"),0;
build(1,n);
modify(l[id[1]],r[id[1]],0);
for (int i=2;i<=tot;++i){
int tl=l[id[i-1]],tr=r[id[i-1]];
int lt=l[id[i]],rt=r[id[i]];
int tmp=query(tl,tr);
l[id[i]]=max(lt,tl);
if (lt>tr) modify(lt,rt,tmp);
else if (rt>tr) modify(tr+1,rt,tmp);
}
printf("%d\n",query(l[id[tot]],r[id[tot]]));
return 0;
}
如有哪裏講得不是很明白或是有錯誤,歡迎指正
如您喜歡的話不妨點個贊收藏一下吧