VP Codeforces Round 876 (Div. 2)


A. The Good Array

题意:一个长度为\(n\)的数组,满足每个\(i\)都有前\(i\)个数和后\(i\)个数有\(\lceil \frac{i}{k} \rceil\)\(1\)。求这样的数组里\(1\)最少的有几个\(1\)

从前往后模拟一遍,从后往前模拟一遍,记录\(1\)的个数就行。

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

    for (int i = n - 1, s = 0; i >= 0; -- i) {
    	s += a[i];
    	if ((n - i + k - 1) / k > s) {
    		a[i] = 1;
    		++ s;
    	}
    }

    std::cout << std::ranges::count(a, 1) << "\n";
}

B. Lamps

题意:\(n\)盏灯,每盏灯有\(a_i, b_i\)两个属性。如果你点亮第\(i\)盏灯,就获得\(b_i\)的收益。如果当前开启了\(k\)盏灯,那么所有\(a_i \leq k\)的灯都会损坏(包括已经亮的灯),已经亮的灯就不算点亮了。你最多对一个灯进行一次操作,求最大收益。

\(a_i\)从小到大点,发现\(a_i\)小的一定先损坏,也就是不会影响比它大的灯损不损坏。那么按\(a_i\)分类,对于\(a_i\)类灯,取前\(a_i\)大的灯就行。

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

    i64 ans = 0;
    for (int i = 1; i <= n; ++ i) {
    	std::ranges::sort(a[i], std::greater<>());
    	for (int j = 0; j < i && j < a[i].size(); ++ j) {
    		ans += a[i][j];
    	}
    }

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

C. Insert Zero and Invert Prefix

题意:你需要\(n\)操作构造一个\(b\)数组:一开始为空,每次在一个位置插入一个\(0\),这个\(0\)前面的数都会取反。求变成\(a\)数组的方案。

观察规律。
从后往前做,取每一段为\(1111..110000..00\)的段,也就是前面若干个\(1\)后面若干个\(0\)。对于这样的段,先放\(cnt_0 + cnt_1 - 1\)\(0\),然后再在第\(cnt_1\)个位置放\(0\),那么就可以变成这个样子。然后往前继续做,这段就会被顶到后面去,最后刚好对齐。

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

    std::ranges::reverse(a);
    if (a[0] == 1) {
    	std::cout << "NO\n";
    	return;
    }

    std::vector<int> ans;
    for (int i = 0; i < n; ++ i) {
    	int j = i;
    	while (j + 1 < n && a[j + 1] == 0) {
    		++ j;
    	}

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

    	int cnt1 = j - i + 1 - cnt0;
    	while (cnt0 > 1) {
    		ans.push_back(0);
    		-- cnt0;
    	}

    	for (int k = 0; k < cnt1; ++ k) {
    		ans.push_back(0);
    	}
    	ans.push_back(cnt1);
    	i = j;
    }

    std::cout << "YES\n";
    for (int i = 0; i < n; ++ i) {
    	std::cout << ans[i] << " \n"[i == n - 1];
    }
}

D. Ball Sorting

题意:给你一个排列,你想要把它变成升序。给你\(k\)\(0\),你可以把它们插入到任意位置,然后每个挨着\(0\)的数可以移动到任意位置。求当\(k = {1, 2, ..., n}\)的时候,最少操作数是多少。

显然每个数只会操作一次。
那么我们希望不操作的数最多,它们肯定组成一个上升子序列,因为它们不动,所以顺序不会变,要想最后有序,那么它们组成的子序列也是有序的。那么我们需要在这个子序列两两之间放\(0\)
\(f[i][j]\)为有\(i\)\(0\),上升子序列目前结尾在\(j\)的最少操作数,那么\(f[i][j] = [a_k < a_j]\min f[i - 1][k] + j - k - 1\)。可以放两个哨兵:\(a_0 = 0, a_{n+1} = inf\)。那么\(f[i][n + 1]\)就是\(k=i\)的答案。细节就是\(a_{j-1} < a_{j}\)那么\(f[i][j] = \min(f[i][j], f[i][j - 1])\)。还有\(f[i][j] = \min(f[i][j], f[i - 1][j])\)

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

    const int inf = 1e9;
    std::vector f(n + 1, std::vector<int>(n + 2, inf));
    f[0][0] = 0;
    for (int i = 1; i <= n + 1; ++ i) {
    	if (a[i] > a[i - 1]) {
    		f[0][i] = 0;
    	} else {
    		break;
    	}
    }
    for (int i = 1; i <= n; ++ i) {
    	for (int j = 1; j <= n + 1; ++ j) {
    		f[i][j] = f[i - 1][j];
    		if (a[j - 1] < a[j]) {
    			f[i][j] = std::min(f[i][j], f[i][j - 1]);
    		}
    		for (int k = 0; k < j; ++ k) {
    			if (a[k] < a[j]) {
    				f[i][j] = std::min(f[i][j], f[i - 1][k] + j - k - 1);
    			}
    		}
    	}
    }

    for (int i = 1; i <= n; ++ i) {
    	std::cout << f[i][n + 1] << " \n"[i == n];
    }
}
posted @ 2025-05-15 16:26  maburb  阅读(20)  评论(0)    收藏  举报