魔 法 魔法 魔法


正 解 部 分 \color{red}{正解部分} 正解部分
设 F [ i ] F[i] F[i] 表示最后一刀切在 i i i 位置, 右端点小于等于 i i i 的所有区间都满足条件的最小花费,
状态转移, 在 i i i 位置切一刀, F [ i ] = min ( F [ l 右 端 点 小 于 i 的 区 间 中 右 端 点 最 大 的 区 间 的 左 端 点 . . . i − 1 ] + A [ i ] ) F[i] = \min(F[l_{右端点小于i的区间中右端点最大的区间的左端点}\ ...\ i-1] + A[i]) F[i]=min(F[l右端点小于i的区间中右端点最大的区间的左端点 ... i−1]+A[i]) .
最后 a n s = min ( F [ l 最 后 一 个 区 间 . . . i − 1 ] ) ans = \min(F[l_{最后一个区间} ... i-1]) ans=min(F[l最后一个区间...i−1]) .

实 现 部 分 \color{red}{实现部分} 实现部分
#include<bits/stdc++.h>
#define reg register
int read(){
char c;
int s = 0, flag = 1;
while((c=getchar()) && !isdigit(c))
if(c == '-'){ flag = -1, c = getchar(); break ; }
while(isdigit(c)) s = s*10 + c-'0', c = getchar();
return s * flag;
}
const int maxn = 2e5 + 10;
const int inf = 0x3f3f3f3f;
int N;
int M;
int len;
int tot;
int A[maxn];
int cf[maxn];
int nxt[maxn];
char S[maxn];
char T[maxn];
struct Intval{ int l, r; } B[maxn*10];
bool cmp(Intval a, Intval b){ return a.r < b.r; }
void get_next(){
nxt[0] = -1;
int i = 0, t = -1;
while(i < len)
if(t == -1 || T[i] == T[t]) nxt[++ i] = ++ t;
else t = nxt[t];
}
void Fuck(){
int t1 = 0, t2 = 0;
while(t1 < N){
if(t2 == -1 || S[t1] == T[t2]) t1 ++, t2 ++;
else t2 = nxt[t2];
if(t2 == len){
// cf[t1-len+1] ++, cf[t1-1+1+1] --;
B[++ tot].l = t1-len+1, B[tot].r = t1;
t2 = nxt[t2];
}
}
}
struct Segment_Tree{
struct Node{ int l, r, v; } T[maxn << 2];
void Build(int k, int l, int r){
T[k].l = l, T[k].r = r, T[k].v = inf;
if(l == r) return ;
int mid = l+r >> 1;
Build(k<<1, l, mid), Build(k<<1|1, mid+1, r);
}
void Add(int k, const int &aim, const int &x){
int l = T[k].l, r = T[k].r;
if(l == r) return T[k].v = x, void();
int mid = l+r >> 1;
if(aim <= mid) Add(k<<1, aim, x);
else Add(k<<1|1, aim, x);
T[k].v = std::min(T[k<<1].v, T[k<<1|1].v);
}
int Query(int k, const int &ql, const int &qr){
int l = T[k].l, r = T[k].r;
if(ql <= l && r <= qr) return T[k].v;
int mid = l+r >> 1, res = inf;
if(ql <= mid) res = Query(k<<1, ql, qr);
if(qr > mid) res = std::min(res, Query(k<<1|1, ql, qr));
return res;
}
} seg_t;
int main(){
// freopen("magic.in", "r", stdin);
// freopen("magic.out", "w", stdout);
N = read(), M = read();
scanf("%s", S);
for(reg int i = 1; i <= N; i ++) A[i] = read();
for(reg int i = 1; i <= M; i ++){
scanf("%s", T);
len = strlen(T);
get_next(); Fuck();
}
// for(reg int i = 1; i <= N; i ++) cf[i] += cf[i-1];
std::sort(B+1, B+tot+1, cmp);
int t = 1, l = 0; seg_t.Build(1, 1, N);
for(reg int i = 1; i <= N; i ++){
if(!l) seg_t.Add(1, i, A[i]);
else seg_t.Add(1, i, A[i]+seg_t.Query(1, l, i-1));
while(t <= tot && B[t].r == i) l = std::max(l, B[t ++].l);
}
printf("%d\n", seg_t.Query(1, l, N));
return 0;
}

本文探讨了一种使用动态规划解决的魔法问题。定义了状态F[i]表示在位置i切割,且所有区间满足条件的最小花费。通过状态转移方程F[i]=min(F[l]+A[i])找到最优解,其中l是右端点小于i的最大区间左端点。最终答案为所有区间上的最小F值。
450

被折叠的 条评论
为什么被折叠?



