• FJOI2021 Day2T1


    (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;
    }
    
  • 相关阅读:
    double保存小数点后两位
    二维数组做函数参数,及地址问题
    解决应用程序无法正常启动0xcxxxxxxxxxx问题
    docker+selenium grid解决node执行经常卡死
    docker+selenium Grid搭建自动化分布式测试环境
    Docker+Selenium Grid+Python搭建分布式测试环境
    xshell无法连接Ubuntu的解决办法
    docker获取镜像很慢解决办法
    django使用ajax传输数据
    $.ajax()所有参数详解
  • 原文地址:https://www.cnblogs.com/Urushibara-Ruka/p/14835598.html
Copyright © 2020-2023  润新知