VP Codeforces Round 904 (Div. 2)


A. Simple Design

题意:找大于等于\(x\)的第一个数位和是\(k\)的倍数的数。

\(k\)很小,则答案不会大于\(x\)很多。暴力枚举即可。

点击查看代码
void solve() {
    int n, k;
    std::cin >> n >> k;
    for (int i = n; ; ++ i) {
    	int x = i, s = 0;
    	while (x) {
    		s += x % 10;
    		x /= 10;
    	}

    	if (s % k == 0) {
    		std::cout << i << "\n";
    		return;
    	}
    }
}

B. Haunted House

题意:给你一个\(01\)串,每次你可以移动相邻的两个数,求对于每个\(i\),让\(1 - i\)位都是\(0\)最小操作数。

我们从低到高模拟,我们每次都要移动一段\(1\),那么我们记录\(1\)的数量,当遇到一个零的时候就代表前面这些1都需要移动一次使得\(cnt_1\)位是0。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::string s;
    std::cin >> s;
    std::vector<i64> ans(n, -1);
    i64 sum = 0, cnt = 0;
    for (int i = n - 1; i >= 0; -- i) {
    	if (s[i] == '1') {
    		++ cnt;
    	} else {
    		sum += cnt;
    		ans[i + cnt] = sum;
    	}
    }

    std::reverse(ans.begin(), ans.end());

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

C. Medium Design

题意:选一些线段,每个线段使得一个区间的数加一。使得最大值减最小值最大。

一个错误的贪心是先求一个最大值的位置,然后不包含这个位置的线段都不选,然后求出来最小值。
这个的\(hack\)\(m = 3, (1, 2), (1, 2), (2, 3), (2, 3), (3, 3)\)。发现不选所有的\((1, 2)\)答案是\(3\),但按照这个贪心不能求出正确答案。
正确思路是记录每个点包含它的线段,以及包含它的线段完整包含了几次整个区间。可以先按线段左端点排序,然后枚举点一个一个线段加,在用个优先队列存右端点,每次删去不合法的对头。
注意需要离散化。

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

    b.push_back(1);
    b.push_back(m);

    std::sort(b.begin(), b.end());
    b.erase(std::unique(b.begin(), b.end()), b.end());

    auto get = [&](int x) -> int {
    	return std::lower_bound(b.begin(), b.end(), x) - b.begin() + 1;
    };

    int k = b.size();
    std::vector<int> d(k + 2);
    for (auto & [l, r] : a) {
    	l = get(l), r = get(r);
    }

    std::sort(a.begin(), a.end());
    using PII = std::pair<int, int>;
    std::priority_queue<PII, std::vector<PII>, std::greater<PII>> heap;
    int pre = 0, suf = 0;
    int ans = 0;
    for (int i = 1, j = 0; i <= k; ++ i) {
    	while (heap.size() && heap.top().first < i) {
    		if (heap.top().second == 1) {
    			-- pre;
    		}
    		heap.pop();
    	}

    	while (j < n && a[j].first == i) {
    		if (a[j].first == 1) {
    			++ pre;
    		}

    		if (a[j].second == k) {
    			++ suf;
    		}
    		heap.push({a[j].second, a[j].first});
    		++ j;
    	}

    	ans = std::max(ans, (int)heap.size() - std::min(pre, suf));
    }

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

D. Counting Rhyme

题意:求\(n\)个数里有多少对数没有共同的因子出现在这些数里面。

\(a_k | a_i, a_k | a_j\)\(a_k | (a_i, a_j)\)
那么我们枚举最大公约数,记\(g_i\)为有多少对数的最大公约数是\(i\)的数量,\(cnt_i\)\(i\)的倍数的数量。
那么有\(g_i = \frac{cnt_i(cnt_i - 1)}{2} - \sum_{i | j} g_j\)。对于一个数\(x\),如果它出现过则所有最大公约数是\(x\)倍数的对都不能选。

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

    std::vector<i64> g(n + 1);
    for (int i = 1; i <= n; ++ i) {
    	for (int j = i + i; j <= n; j += i) {
    		cnt[i] += cnt[j];
    	}
    }

    for (int i = n; i >= 1; -- i) {
    	g[i] = cnt[i] * (cnt[i] - 1) / 2;
    	for (int j = i + i; j <= n; j += i) {
    		g[i] -= g[j];
    	}
    }

    for (int i = 1; i <= n; ++ i) {
    	if (st[i]) {
    		for (int j = i; j <= n; j += i) {
    			g[j] = 0;
    		}
    	}
    }

    i64 ans = std::accumulate(g.begin(), g.end(), 0ll);
    std::cout << ans << "\n";
}
posted @ 2025-03-04 22:23  maburb  阅读(17)  评论(0)    收藏  举报