• GDKOI 2021 提高组 Day2 第三题 抄写(manachar+线段树维护DP)


    GDKOI 2021 提高组 Day2 第三题 抄写

    题目大意

    • 要求按顺序写完一段小写字母组成的长为 n n n的字符串,可以直接添加一个字符 i i i,代价为 c i c_i ci,也可以用当前末尾的部分轴对称,代价为 C C C,求最小代价。
    • n ≤ 1 0 6 nle10^6 n106

    题解

    • 先考虑 n 2 n^2 n2的DP转移, f i = m i n ( f i − 1 + c i , f j + C ) f_i=min(f_{i-1}+c_i,f_j+C) fi=min(fi1+ci,fj+C),其中 j j j是能作为对称轴的位置,可以用manachar预处理, O ( 1 ) O(1) O(1)判断合法的 j j j
    • 观察到转移的贡献都是一样的 C C C,那么可以直接从对称轴向后转移,用线段树用 l o g log log的时间区间修改,避免了 O ( n ) O(n) O(n)的转移。

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define N 1000010
    #define ll long long
    ll f[N], g[N * 4];
    int c[26], s0[N], s1[N];
    char a[N];
    void make(int v, int l, int r) {
    	g[v] = 1e16;
    	if(l == r) return ;
    	int mid = (l + r) / 2;
    	make(v * 2, l, mid), make(v * 2 + 1, mid + 1, r);
    }
    ll find(int v, int l, int r, int x) {
    	if(l == r) return g[v];
    	int mid = (l + r) / 2;
    	g[v * 2] = min(g[v], g[v * 2]), g[v * 2 + 1] = min(g[v], g[v * 2 + 1]);
    	if(x <= mid) return find(v * 2, l, mid, x);
    	return find(v * 2 + 1, mid + 1, r, x);
    }
    void ins(int v, int l, int r, int x, int y, ll s) {
    	if(l == x && r == y) {
    		g[v] = min(g[v], s);
    	}
    	else {
    		int mid = (l + r) / 2;
    		g[v * 2] = min(g[v], g[v * 2]), g[v * 2 + 1] = min(g[v], g[v * 2 + 1]);
    		if(y <= mid) ins(v * 2, l, mid, x, y, s);
    		else if(x > mid) ins(v * 2 + 1, mid + 1, r, x, y, s);
    		else ins(v * 2, l, mid, x, mid, s), ins(v * 2 + 1, mid + 1, r, mid + 1, y, s);
    	}
    }
    int main() {
    	int n, C, i, j;
    	scanf("%d%d", &n, &C);
    	for(i = 0; i < 26; i++) scanf("%d", &c[i]);
    	scanf("
    ");
    	scanf("%s", a + 1);
    	int l = 1, r = 0, mid;
    	for(i = 1; i <= n; i++) {
    		s1[i] = 1, s0[i] = 0;
    		if(l <= r && i <= r) {
    			if((r - l + 1) % 2 == 0) {
    				s0[i] = min(s0[2 * mid - i], r - i);
    				s1[i] = min(s1[2 * mid - i + 1], r - i + 1);
    			}
    			else {
    				s0[i] = min(s0[2 * mid - i - 1], r - i);
    				s1[i] = min(s1[2 * mid - i], r - i + 1);
    			}
    		}
    		while(i - s0[i] > 0 && i + s0[i] < n && a[i - s0[i]] == a[i + s0[i] + 1]) s0[i]++;
    		while(i - s1[i] > 0 && i + s1[i] <= n && a[i - s1[i]] == a[i + s1[i]]) s1[i]++;
    		if(i + s1[i] - 1 > r) l = i - s1[i] + 1, r = i + s1[i] - 1, mid = i;
    		if(i + s0[i] > r) l = i - s0[i] + 1, r = i + s0[i], mid = i;
    	} 
    	f[0] = 0;
    	make(1, 1, n);
    	for(i = 1; i <= n; i++) {
    		f[i] = find(1, 1, n, i);
    		f[i] = min(f[i], f[i - 1] + (c[a[i] - 'a']));
    		if(s1[i] > 1) ins(1, 1, n, i + 1, i + s1[i] - 1, f[i] + C);
    		if(s0[i]) ins(1, 1, n, i + 1, i + s0[i], f[i] + C);
    	}
    	printf("%lld
    ", f[n]);
    	return 0;
    }
    

    自我小结

    • 考场上manachar维护最靠右的回文串写成了维护最长的回文串,挂了30分。忘记了的话可以想到右端点一定是单调向右移动的,这样才能保证 O ( n ) O(n) O(n)的复杂度。

    • 可见,复习算法很重要。

    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    ubuntu下python的错误
    Zookeeper(二) zookeeper集群搭建 与使用
    Zookeeper(一) zookeeper基础使用
    MapReduce(五) mapreduce的shuffle机制 与 Yarn
    MapReduce(四) 典型编程场景(二)
    Mysql(一) 基本操作
    MapReduce(三) 典型场景(一)
    MapReduce(二)常用三大组件
    MapReduce(一) mapreduce基础入门
    Hive(六)hive执行过程实例分析与hive优化策略
  • 原文地址:https://www.cnblogs.com/LZA119/p/14437602.html
Copyright © 2020-2023  润新知