VP Educational Codeforces Round 62 (Rated for Div. 2)


A. Detective Book

题意:给你一个数组\(a\),从\(1\)开始,每次跳到\(a_i\),直到\(a_i = i\)算一轮,然后位置往后移一位。求总共几轮。

模拟。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    	-- a[i];
    }

    int ans = 0;
    int i = 0;
    while (i < n) {
    	++ ans;
    	int j = a[i];
    	while (i <= j) {
			j = std::max(j, a[i]);
			++ i;
    	}
    }

    std::cout << ans << "\n";
}

B. Good String

题意:'<'$可以删去其左边的所以字符,'>'可以删去右边所有字符。求最少删去几个字符使得可以一次操作删除字符串。

看左边连续的'<'和右边连续的'>'的长度的最小值。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::string s;
    std::cin >> s;
    int l = 0, r = n - 1;
    while (l < n && s[l] == '<') {
    	++ l;
    }

    while (r >= 0 && s[r] == '>') {
    	-- r;
    }

    std::cout << std::min(l, n - 1 - r) << "\n";
}

C. Playlist

题意:\(n\)个物品,每个物品有长度和价值,最多选\(k\)个物品,价值为总长度乘最小价值。求最大价值。

按价值从大到小排序,然后用小根堆维护前\(k\)大。如果不足\(k\)个直接选前\(k\)个就行。
如果选\(k\)个,那么就是典题,我们选择了最小价值的情况下肯定选大于等于这个价值的前\(k\)个长度最大的。如果是选了小于\(k\)个,那么也是枚举最小价值,此时因为不足\(k\)个,那么总长度越大越好,所以全部选上。

点击查看代码
void solve() {
    int n, k;
    std::cin >> n >> k;
    std::vector<std::pair<int, int>> a(n);
    for (int i = 0; i < n; ++ i) {
    	int t, v;
    	std::cin >> t >> v;
    	a[i] = {v, t};
    }

    std::ranges::sort(a, std::greater<>());
    std::priority_queue<int, std::vector<int>, std::greater<int>> heap;
    i64 ans = 0, sum = 0;
    for (auto & [v, t] : a) {
    	if (heap.size() == k) {
    		if (heap.top() < t) {
    			sum = sum - heap.top() + t;
    			heap.pop();
    			heap.push(t);
    		}
    	} else {
    		sum += t;
    		heap.push(t);	 
    	}

		ans = std::max(ans, sum * v);
    }

    std::cout << ans << "\n";
}

D. Minimum Triangulation

区间dp板子题。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    const i64 inf = 1e18;
    std::vector f(n + 1, std::vector<i64>(n + 1));
    for (int len = 3; len <= n; ++ len) {
    	for (int i = 1; i + len - 1 <= n; ++ i) {
    		int j = i + len - 1;
    		f[i][j] = inf;
    		for (int k = i + 1; k < j; ++ k) {
    			f[i][j] = std::min(f[i][j], f[i][k] + f[k][j] + (i64)i * k * j);
    		}
    	}
    }

    std::cout << f[1][n] << "\n";
}

E. Palindrome-less Arrays

题意:有一个数组,值域在\([1, k]\),有些地方填了,有些没填。如果这个数组满足没有一个长度大于1的奇数回文子数组就是好的。求好数组个数。

奇数回文子数组一定有一个长度为\(3\)的回文子数组。也就是\(a_i \ne a_{i+2}\)。那么发现奇偶性相同的位置需要一起讨论。那么把原数组拆成两个数组,分别求出方案数然后相乘就行。
那么问题变成了一个数组有些地方没填要求相邻数不能相等。考虑把所有没填的连续的段拿出来,显然它和其它段互不影响,分别求出来然后乘起来就行。
那么预处理一个数组\(f[i][0/1]\)表示长度为\(i\)的没填的段两边相不相等的方案数,\(f[i][0] = (k - 1) \times f[i - 1][1], f[i][1] = (k - 2) * f[i - 1][1] + f[i - 1][0]\)。转移方程意思就是讨论在某尾放什么数。

然后还有四种特性情况,一种是没有没填的位置,这个判断就行。
一种是没填的连续段作为数组开头,那么枚举第一个数填什么,得到\(f[len - 1][0] + f[len - 1][1] * (k - 1)\)。结尾是连续段的同理。
还有就是全是没填的。那么是\(k\times(k-1)^{n-1}\)

点击查看代码
const int mod = 998244353;

int power(int a, int b) {
	int res = 1;
	for (; b ; b >>= 1, a = 1LL * a * a % mod) {
		if (b & 1) {
			res = 1LL * res * a % mod;
		}
	}

	return res;
}

void solve() {
    int n, k;
    std::cin >> n >> k;
    std::vector<int> a, b;
    for (int i = 0; i < n; ++ i) {
    	int x;
    	std::cin >> x;
    	if (i & 1) {
    		a.push_back(x);
    	} else {
    		b.push_back(x);
    	}
    }

    std::vector f(n + 1, std::array<int, 2>{0, 0});
    f[0][0] = 0, f[0][1] = 1;
    for (int i = 1; i <= n; ++ i) {
    	f[i][0] = (i64)(k - 1) * f[i - 1][1] % mod;
    	f[i][1] = ((i64)(k - 2) * f[i - 1][1] % mod + f[i - 1][0]) % mod;
    }

    auto work = [&](std::vector<int> & a) -> int {
    	int res = 1;
    	int n = a.size();
    	if (std::ranges::count(a, -1) == n) {
    		res = (i64)k * power(k - 1, n - 1) % mod;
    		return res;
    	}

    	for (int i = 0; i < n; ++ i) {
    		if (a[i] == -1) {
    			int j = i;
    			while (j < n && a[j] == -1) {
    				++ j;
    			}

    			int len = j - i;
    			if (i == 0 || j == n) {
    				res = (i64)(f[len - 1][0] + (i64)(k - 1) * f[len - 1][1] % mod) % mod * res % mod;
    			} else {
    				if (a[i - 1] == a[j]) {
    					res = (i64)res * f[len][0] % mod;
    				} else {
    					res = (i64)res * f[len][1] % mod;
    				}
    			}

    			i = j;
    		} else {
    			if (i && a[i] == a[i - 1]) {
    				return 0;
    			}
    		}
    	}

    	return res;
    };

    int ans = (i64)work(a) * work(b) % mod;
    std::cout << ans << "\n";
}
posted @ 2025-04-18 16:04  maburb  阅读(18)  评论(0)    收藏  举报