(T) 组询问,每次给定一个串 (S) ,和 (m) 个串 (t_i)
要求切割一些 (t) 串去拼接成 (S) 串,每次切割你可以选择串 (t_i) 的一个前缀或一个后缀,代价为 (c_i)
最小化拼接成 (S) 的代价
这题要清空的东西实在是巨多,一不小心就完蛋了
话说现在来写题解是不是有亿点点晚了233
一个比较简单的(mathcal O(|S| ^ 2))思路是dp+hash,设(dp[i])为拼接出(S[1...i])需要的最小代价
但是要过(30pts),也就是(10000)那档的话,冲突概率可能比较大,要双哈希挂链法(没有写不知道,对拍的时候直接用map了hhhhh)
发现
如果用填表法转移dp,那么用后缀去拼接就比较轻松,只用后缀的话,会有大量决策点转移到该点的所需的(c_i)相同,它们组成若干段连续的区间,只要每个区间内取最小值即可
如果用刷表法转移dp,那么只用前缀的话,它也会有大量转移代价相同,同样形成若干段连续区间,然后我们区间更新
所以dp时填表法和刷表法同时进行(这好像是这题卡我这只蒟蒻最大的一个点了2333),但这样看上去是不是像个(mathcal O(n^2log n))的暴力优化(((((
然后我就想着把广义sam建出来然后再搞搞
对(t)正串反串分别建广义sam(总共两只sam)
((vis)是把字符转换成数字,我大FJ的惯例是字符集为({A,C,G,T}))
for (int i = 1; i <= m; i++) {
scanf("%s%d", t + 1, &cst); len = strlen(t + 1);
a.las = 1; for (int j = 1; j <= len; j++) a.ins(vis[t[j]]); a.val[a.las] = min(a.val[a.las], (ll)cst);
b.las = 1; for (int j = len; j >= 1; j--) b.ins(vis[t[j]]); b.val[b.las] = min(b.val[b.las], (ll)cst);
}
发现parent树上代价相同的父亲方向的一段链,只需要合起来考虑一次,这样好像就只需要(sqrt{n})次转移(证明就类似长链剖分的那个(sqrt{n})段的证明)
可是(200000)(还是(300000)???我忘了)跑(mathcal O(nsqrt{n}log n))(令(n=|S|))还是有点糟糕。。
何况这还是我大FJ
想了很久确实还是不怎么会,于是就开始卡常了
把该点设为(u),把每次跳到父亲方向代价((cost[v]))小于(cost[u])的第一个位置(v) 改成 每次找(lastv ightarrow u)这段链(排除(lastv))上代价最小的(v),然后把下次寻找的链变为(v ightarrow u)
倍增改树剖+ST表,线段树(我实在想zkw线段树一波,可是不会=_=)
由于不知道是数据水还是复杂度有更低的界过了,有没有哥哥可以解释一下这个傻逼做法的真实复杂度啊
考场代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5 + 10;
int T, n, m, len, cst, vis[300]; char s[N], t[N];
struct sam {
int fa[N << 1], tr[N << 1][4], len[N << 1], nodeid, las; ll val[N << 1];
void init() {
nodeid = las = 1, val[1] = 1ll << 60;
memset(tr[1], 0, sizeof(tr[1]));
}
void ins(int c) {
int p = las;
if (tr[p][c]) {
int q = tr[p][c];
if (len[q] == len[p] + 1) las = q;
else {
int nq = ++nodeid; len[nq] = len[p] + 1, val[nq] = 1ll << 60;
memcpy(tr[nq], tr[q], sizeof(tr[nq]));
fa[nq] = fa[q], fa[q] = nq;
for ( ; p && tr[p][c] == q; p = fa[p]) tr[p][c] = nq;
las = nq;
}
return ;
}
int np = ++nodeid; len[np] = len[p] + 1, val[np] = 1ll << 60;
memset(tr[np], 0, sizeof(tr[np]));
for ( ; p && !tr[p][c]; p = fa[p]) tr[p][c] = np;
if (!p) fa[np] = 1;
else {
int q = tr[p][c];
if (len[q] == len[p] + 1) fa[np] = q;
else {
int nq = ++nodeid; len[nq] = len[p] + 1, val[nq] = 1ll << 60;
memcpy(tr[nq], tr[q], sizeof(tr[nq]));
fa[nq] = fa[q], fa[q] = fa[np] = nq;
for ( ; p && tr[p][c] == q; p = fa[p]) tr[p][c] = nq;
}
}
las = np;
}
int head[N << 1], to[N << 1], nxt[N << 1], cnt;
void add_edge(int u, int v) {
to[++cnt] = v, nxt[cnt] = head[u], head[u] = cnt;
}
int hea[N << 1], sze[N << 1], dfn[N << 1], Top[N << 1], times, pos[20][N << 1], Log[N << 1];
void dfs(int u) {
sze[u] = 1;
for (int i = head[u]; i; i = nxt[i]) {
int v = to[i];
dfs(v), sze[u] += sze[v];
if (sze[hea[u]] < sze[v]) hea[u] = v;
if (val[u] > val[v]) val[u] = val[v];
}
}
void pre(int u, int t) {
dfn[u] = ++times, Top[u] = t, pos[0][times] = u;
if (hea[u]) pre(hea[u], t);
for (int i = head[u]; i; i = nxt[i]) if (to[i] != hea[u]) pre(to[i], to[i]);
}
void clean() {
for (int i = 1; i <= nodeid; i++) head[i] = hea[i] = 0;
cnt = times = 0;
for (int i = 2; i <= nodeid; i++) add_edge(fa[i], i), Log[i] = Log[i >> 1] + 1;
dfs(1), pre(1, 1);
for (int k = 1; (1 << k) <= nodeid; k++)
for (int i = 1; i + (1 << k) - 1 <= nodeid; i++)
if (val[pos[k - 1][i]] < val[pos[k - 1][i + (1 << (k - 1))]]) pos[k][i] = pos[k - 1][i];
else pos[k][i] = pos[k - 1][i + (1 << (k - 1))];
}
int MIN(int l, int r) {
int t = Log[r - l + 1];
if (val[pos[t][l]] < val[pos[t][r - (1 << t) + 1]]) return pos[t][l];
return pos[t][r - (1 << t) + 1];
}
int find(int u, int g) {
u = fa[u];
ll MI = 1ll << 60; int p = -1;
while (len[Top[u]] > len[g]) {
int now = MIN(dfn[Top[u]], dfn[u]);
if (MI > val[now]) MI = val[now], p = now;
u = fa[Top[u]];
}
if (u != g) {
int now = MIN(dfn[g] + 1, dfn[u]);
if (MI > val[now]) MI = val[now], p = now;
}
return p;
}
} a, b;
#define lson u << 1, l, mid
#define rson u << 1 | 1, mid + 1, r
ll Min[N << 2], tag[N << 2], dp[N];
void build(int u, int l, int r) {
Min[u] = tag[u] = 1ll << 60;
int mid = (l + r) >> 1;
if (l < r) build(lson), build(rson);
}
void pushdown(int u) {
if (tag[u] != (1ll << 60)) {
int ls = u << 1, rs = u << 1 | 1;
if (Min[ls] > tag[u]) Min[ls] = tag[u];
if (Min[rs] > tag[u]) Min[rs] = tag[u];
if (tag[ls] > tag[u]) tag[ls] = tag[u];
if (tag[rs] > tag[u]) tag[rs] = tag[u];
tag[u] = 1ll << 60;
}
}
void modify(int u, int l, int r, int Left, int Right, ll d) {
if (Left <= l && r <= Right) Min[u] = min(Min[u], d), tag[u] = min(tag[u], d);
else {
int mid = (l + r) >> 1; pushdown(u);
if (Left <= mid) modify(lson, Left, Right, d);
if (mid < Right) modify(rson, Left, Right, d);
Min[u] = min(Min[u << 1], Min[u << 1 | 1]);
}
}
ll query(int u, int l, int r, int Left, int Right) {
if (Left <= l && r <= Right) return Min[u];
int mid = (l + r) >> 1; ll res = 1ll << 60; pushdown(u);
if (Left <= mid) res = min(res, query(lson, Left, Right));
if (mid < Right) res = min(res, query(rson, Left, Right));
return res;
}
int id[N], le[N];
int main() {
freopen("gene.in", "r", stdin);
freopen("gene.out", "w", stdout);
vis['A'] = 0, vis['C'] = 1, vis['G'] = 2, vis['T'] = 3;
scanf("%d", &T);
while (T--) {
scanf("%s", s + 1); n = strlen(s + 1);
scanf("%d", &m); a.init(), b.init();
for (int i = 1; i <= m; i++) {
scanf("%s%d", t + 1, &cst); len = strlen(t + 1);
a.las = 1; for (int j = 1; j <= len; j++) a.ins(vis[t[j]]); a.val[a.las] = min(a.val[a.las], (ll)cst);
b.las = 1; for (int j = len; j >= 1; j--) b.ins(vis[t[j]]); b.val[b.las] = min(b.val[b.las], (ll)cst);
}
a.clean(), b.clean();
int now = 1, lll = 0;
for (int i = n; i >= 1; i--) {
int c = vis[s[i]];
while (now > 1 && !b.tr[now][c]) now = b.fa[now], lll = b.len[now];
le[i] = ++lll, id[i] = now = b.tr[now][c];
}
now = 1, lll = 0, build(1, 0, n);
for (int i = 1; i <= n; i++) {
modify(1, 0, n, i - 1, i - 1, dp[i - 1]);
int p = 1;
if (le[i] > 0) modify(1, 0, n, i, i + le[i] - 1, dp[i - 1] + b.val[id[i]]);
while (true) {
p = b.find(id[i], p); if (p == -1) break;
modify(1, 0, n, i, i + b.len[p] - 1, dp[i - 1] + b.val[p]);
}
int c = vis[s[i]];
while (now > 1 && !a.tr[now][c]) now = a.fa[now], lll = a.len[now];
++lll, now = a.tr[now][c];
if (lll == 0) dp[i] = query(1, 0, n, i, i);
else dp[i] = min(query(1, 0, n, i, i), query(1, 0, n, i - lll, i - 1) + a.val[now]);
p = 1;
while (true) {
p = a.find(now, p); if (p == -1) break;
dp[i] = min(dp[i], query(1, 0, n, i - a.len[p], i - 1) + a.val[p]);
}
}
if (dp[n] == (1ll << 60)) puts("-1");
else printf("%lld
", dp[n]);
}
return 0;
}