VP Educational Codeforces Round 30


A. Chores

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

    int ans = k * x;
    for (int i = 0; i < n - k; ++ i) {
    	ans += a[i];
    }

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

B. Balanced Substring

题意:求一个\(01\)串最长的\(0\)\(1\)个数相同的子串。

\(1\)的位置加一,\(0\)的位置减一,记录相同的前缀和。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::string s;
    std::cin >> s;

    std::vector<int> sum(n + 1);
    for (int i = 0; i < n; ++ i) {
    	sum[i + 1] = sum[i] + (s[i] == '1' ? 1 : -1);
    }

    std::map<int, int> mp;
    int ans = 0;
    mp[0] = 0;
    for (int i = 1; i <= n; ++ i) {
    	if (mp.count(sum[i])) {
    		ans = std::max(ans, i - mp[sum[i]]);
    	} else {
    		mp[sum[i]] = i;
    	}
    }

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

C. Strange Game On Matrix

题意:每一列删去最顶上的若干个一,然后从剩下的最高的一往下数最多\(k\)个,使得和最大已经修改的数最少。

模拟题,每一列搞个前缀和,然后枚举选中的第一个一。

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

	int ans = 0, cnt = 0;
	for (int j = 1; j <= m; ++ j) {
		int max = 0, del = 0;
		for (int i = 1; i <= n; ++ i) {
			if (a[i][j] == 1) {
				int x = std::min(n, i + k - 1);
				if (sum[x][j] - sum[i - 1][j] > max) {
					max = sum[x][j] - sum[i - 1][j];
					del = sum[i - 1][j];
				}
			}
		}

		ans += max;
		cnt += del;
	}

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

D. Merge Sort

题意:对于归并排序,如果当前区间已经有序则返回,否则递归左右子区间。构造一个排列使得调用函数的总次数为\(k\)

次数总为奇数,因为最开始调用了一次,然后如果要继续递归则两个子区间都会调用一次。
那么我们可以交换\(mid - 1, mid\)这两个位置,这样左右区间依然有序,并且当前区间无序,就可以向下递归。

点击查看代码
void solve() {
    int n, k;
    std::cin >> n >> k;
    std::vector<int> a(n);
    std::iota(a.begin(), a.end(), 1);

    auto dfs = [&](auto & self, int l, int r) -> void {
    	if (l + 1 == r || k == 0) {
    		return;
    	}

    	k -= 2;

    	int mid = l + r >> 1;
		std::swap(a[mid - 1], a[mid]);

		self(self, l, mid);
		self(self, mid, r);
    };	

    k -= 1;
    dfs(dfs, 0, n);
    if (k == 0) {
    	for (int i = 0; i < n; ++ i) {
    		std::cout << a[i] << " \n"[i == n - 1];
    	}
    } else {
    	std::cout << -1 << "\n";
    }
}

E. Awards For Contestants

题意:数组从大到小排序后,把区间分成\(4\)段,第\(4\)段长度可以为\(0\)。要求前三段的长度都互相不超过另外两段的两倍,同时要求第一段最小值减第二段最大值最大,满足上面条件还要第二段最小值减第三段最大值最大,同时要求第三段最小值减第四段最大值最大。

可以枚举前两段,那么第三段可以取的区间范围也可以算出来。然后第三段的结尾我们肯定要减下一个数最大的位置,可以用线段树维护。
注意如果第四段为空,则第三段减第四段的值等于第三段的最小值。

点击查看代码
#define ls (u << 1)
#define rs (u << 1 | 1)
#define umid (tr[u].l + tr[u].r >> 1)

template <class Info>
struct Node {
	int l, r;
	Info info;
};

template <class Info>
struct SegmentTree {
	std::vector<Node<Info> > tr;
	SegmentTree(int _n) {
		init(_n);
	}

	SegmentTree(std::vector<Info> & a) {
		init(a);
	}

	void init(int _n) {
		tr.assign(_n << 2, {});
		build(0, _n - 1);
	}

	void init(std::vector<Info> & a) {
		int _n = (int)a.size();
		tr.assign(_n << 2, {});
		build(0, _n - 1, a);
	}

	void pushup(int u) {
		tr[u].info = tr[ls].info + tr[rs].info;
	}

	void build(int l, int r, int u = 1) {
		tr[u] = {l, r, {}};
		if (l == r) {
			return;
		}

		int mid = l + r >> 1;
		build(l, mid, ls); build(mid + 1, r, rs);
	}

	void build(int l, int r, std::vector<Info> & a, int u = 1) {
		tr[u] = {l, r, {}};
		if (l == r) {
			tr[u].info = a[l];
			return;
		}

		int mid = l + r >> 1;
		build(l, mid, a, ls); build(mid + 1, r, a, rs);
		pushup(u);
	}

	void modify(int p, Info add, bool set = false) {
		int u = 1;
		while (tr[u].l != tr[u].r) {
			int mid = umid;
			if (p <= mid) {
				u = ls;
			} else {
				u = rs;
			}
		}

		if (set) {
			tr[u].info = add;
		} else {
			tr[u].info = tr[u].info + add;
		}

		u >>= 1;
		while (u) {
			pushup(u);
			u >>= 1;
		}
	}

	Info query(int l, int r, int u = 1) {
		if (l <= tr[u].l && tr[u].r <= r) {
			return tr[u].info;
		}

		int mid = umid;
		if (r <= mid) {
			return query(l, r, ls);
		}  else if (l > mid) {
			return query(l, r, rs);
		}

		return query(l, r, ls) + query(l, r, rs);
	}
};

struct Info {
	int max, p;
};

Info operator + (const Info & a, const Info & b) {
	Info res{};
	if (a.max >= b.max) {
		res = a;
	} else {
		res = b;
	}

	return res;
}

void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    std::vector<int> id(n);
    std::iota(id.begin(), id.end(), 0);
    std::sort(id.begin(), id.end(), [&](int i, int j) {
    	return a[i] > a[j];
    });


    SegmentTree<Info> tr(n);
    tr.modify(n - 1, Info{a[id[n - 1]], n - 1}, true);
    for (int i = n - 2; i >= 0; -- i) {
    	tr.modify(i, Info{a[id[i]] - a[id[i + 1]], i}, true);
    }


    int p1 = 0, p2 = 1, p3 = 2;
    int max1 = -1, max2 = -1, max3 = -1;
    for (int i = 0; i + 2 < n; ++ i) {
    	for (int j = i + 1; j + 1 < n; ++ j) {
    		if ((i + 1) > 2 * (j - i) || j - i > 2 * (i + 1)) {
    			continue;
    		}

    		int l = j + (std::max(i + 1, j - i) + 1) / 2, r = std::min(n - 1, j + std::min(i + 1, j - i) * 2);
    		if (l > r) {
    			continue;
    		}

    		auto [v3, p] = tr.query(l, r);
    		int v1 = a[id[i]] - a[id[i + 1]], v2 = a[id[j]] - a[id[j + 1]];
    		if (v1 > max1 || (v1 == max1 && v2 > max2) || (v1 == max1 && v2 == max2 && v3 > max3)) {
    			max1 = v1;
    			max2 = v2;
    			max3 = v3;
    			p1 = i;
    			p2 = j;
    			p3 = p;
    		}
    	}
    }

    std::vector<int> ans(n, -1);
    for (int i = 0; i <= p1; ++ i) {
    	ans[id[i]] = 1;
    }

    for (int i = p1 + 1; i <= p2; ++ i) {
    	ans[id[i]] = 2;
    }

    for (int i = p2 + 1; i <= p3; ++ i) {
    	ans[id[i]] = 3;
    }

    for (int i = 0; i < n; ++ i) {
    	std::cout << ans[i] << " \n"[i == n - 1];
    }
}
posted @ 2025-03-11 06:19  maburb  阅读(22)  评论(0)    收藏  举报