Educational Codeforces Round 176 (Rated for Div. 2)


A. To Zero

题意:给你一个\(n\)\(k\), \(k\)是一个奇数,你每次可以选择\(1\)\(k\)的某个数让\(n\)减掉。但如果\(n\)是奇数你也必须选奇数,是偶数你也必须选偶数。

如果\(n\)是奇数,我们就减去\(k\), 因为奇数减奇数后就是偶数,不能再减回奇数,所以应该在能减奇数的时候减。然后就是一直减\(k-1\),直到\(n \leq k - 1\),就减去\(n\)就行了。

点击查看代码
void solve() {
    int n, k;
    std::cin >> n >> k;
    int ans = 0;
    if (n & 1) {
    	n -= k;
    	++ ans;
    }

    ans += (n + k - 2) / (k - 1);
    std::cout << ans << "\n";
}

B. Array Recoloring

题意:给你\(n\)个数,一开始你要选\(k\)个数填色,然后得到它们的总和的价值。最后你要一步一步扩展这\(k\)个数,每次可以将一个相邻的位置填色,你可以得到最后填色的位置的价值。求最大价值。

我们可以得到\(k+1\)个数,但直接取最大的\(k+1\)个是不行的,因为可能选了\(k\)个最大的后第\(k+1\)大的不能被最后选到。
我们可以枚举选的数的左右边界,也就是对于\([i, j]\)我们必选\(a_i, a_j\),那么只需要算出区间里的前\(k-1\)大的数就行了。可以用优先队列维护。

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


    i64 ans = 0;
    
    if (k == 1) {
    	for (int i = 1; i < n; ++ i) {
    		ans = std::max(ans, a[i] + a[0]);
    	}

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

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

    for (int i = 0; i < n; ++ i) {
    	std::priority_queue<i64, std::vector<i64>, std::greater<i64>> heap;
    	i64 sum = 0;
    	for (int j = i + 1; j < n; ++ j) {
    		if (j - i - 1 >= k - 1) {
    			ans = std::max(ans, sum + a[i] + a[j]);
    		} 

    		if (heap.size() == k - 1) {
    			if (k != 1 && a[j] > heap.top()) {
    				sum -= heap.top();
    				heap.pop();
    				sum += a[j];
    				heap.push(a[j]);
    			}
    		} else {
    			sum += a[j];
    			heap.push(a[j]);
    		}

    	}
    }

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

C. Two Colors

题意:有长度为\(n\)个木板和\(m\)中颜色,每种颜色只能涂成一个区间,并且每个颜色的数量是\(a_i\)。你要把木板填成两种颜色。问有多少种填法。

对于一个\(a_i\),和它一起填木板的\(a_j\)至少是\(n-a_i\)。然后模拟一下,如果规定\(i\)涂左边,\(j\)涂右边,那么长度为\(a_j\)的板子和\(a_i\)\(a_j - (n - a_i - 1)\)种填法。那么我们可以记录\(a_i\)\(k\)的颜色的个数,已经\(a_i\)\(k\)\(a_i\)的和,用树状数组维护维护前缀和。我们从前往后枚举,那么对于一个数量为\(a_i\)的颜色,贡献为\(sum(n - a_i, n) - cnt(n - a_i, n) \times (n - a_i - 1)\)。注意要特别\(a_i\)等于\(n\)的颜色的贡献。

点击查看代码
template <class T>
struct Fenwick {
    int n;
    std::vector<T> tr;

    Fenwick(int _n) {
        init(_n);
    }

    void init(int _n) {
        n = _n;
        tr.assign(_n + 1, T{});
    }

    void add(int x, const T &v) {
        for (int i = x; i <= n; i += i & -i) {
            tr[i] = tr[i] + v;
        }
    }

    T query(int x) {
        T res{};
        for (int i = x; i; i -= i & -i) {
            res = res + tr[i];
        }
        return res;
    }

    T sum(int l, int r) {
        return query(r) - query(l - 1);
    }
};

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

    Fenwick<i64> sum(n + 1), cnt(n + 1);
    i64 ans = 0;
    for (int i = 0; i < m; ++ i) {
    	if (a[i] != n) {		
	    	ans += sum.sum(n - a[i], n - 1) - cnt.sum(n - a[i], n - 1) * (i64)(n - a[i] - 1);
	    	ans += cnt.sum(n, n) * a[i];
    	} else {
    		ans += sum.sum(1, n - 1) + cnt.sum(n, n) * (n - 1);
    	}
    	sum.add(a[i], a[i]);
    	cnt.add(a[i], 1);
    }

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

D. Equalization

题意:给你一个\(x, y\),你每次可以选一个\(k\),使得\(x\)或者\(y\)除以\(2^k\)向下取整。每个\(k\)只能选一次,且代价为\(2^k\),求\(x,y\)相等的最小代价。

在二进制下观察,除\(2^k\)就是右移\(k\)位。那么我们从高到低看,假设\(x\)\(n\)位,\(y\)\(m\)位,那么如果对于\(i \in [1, \min(n, m)]\)\(x_n\)\(x_{n-i+1}\)等于\(y_m\)\(y_{m-i+1}\), 则我们可以使\(x\)右移\(n-i\)位和\(y\)右移\(m-i\)为使得它们相等。
那么我们记\(f[i][j]\)\(x\)右移\(i\)\(y\)右移\(j\)位的最小代价。那么这是一个背包问题:有两个容量,一些物品,要求每个物品只能用一次,使得正好装满两个容量的最小代价。那么我们可以\(01\)背包预处理。
然后求出\(x,y\)的二进制表示,按上述从高到低看,取最小值。然后假设结果是\(x, y\)都变成\(0\),那么不必恰好右移多少位,可以枚举右移数大于\(n, m\)的答案。最后如果某个数二进制表示是另一个数的前缀,也要特殊处理一下。

点击查看代码
const int N = 60;

i64 f[N][N];

void init() {
	for (int i = 0; i < N; ++ i) {
		for (int j = 0; j < N; ++ j) {
			f[i][j] = 1e18;
		}
	}
	f[0][0] = 0;
	for (int k = 1; k < N; ++ k) {
		for (int i = N - 1; i >= 0; -- i) {
			for (int j = N - 1; j >= 0; -- j) {
				if (i >= k) {
					f[i][j] = std::min(f[i][j], f[i - k][j] + (1ll << k));
				}

				if (j >= k) {
					f[i][j] = std::min(f[i][j], f[i][j - k] + (1ll << k));
				}
			}	
		}
	}
}

void solve() {
    i64 x, y;
    std::cin >> x >> y;
    if (x == y) {
    	std::cout << 0 << "\n";
    	return;
    }

    std::vector<int> a, b;
    while (x) {
    	a.push_back(x % 2);
    	x /= 2;
    }

    while (y) {
    	b.push_back(y % 2);
    	y /= 2;
    }

    int n = a.size(), m = b.size();
    i64 ans = 1e18;
    for (int i = N - 1; i >= std::max(n, m); -- i) {
    	ans = std::min({ans, f[i][i], f[n][i], f[i][m]});
    }

    bool flag = true;
	for (int i = n, j = m; i >= 1 && j >= 1; -- i, -- j) {
		ans = std::min(ans, f[i][j]);
		if (a[i - 1] != b[j - 1]) {
			flag = false;
			break;
		}
	}

	if (flag) {
		ans = std::min(ans, f[0][std::abs(n - m)]);
	}

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


int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int T = 1; 
	init();
	std::cin >> T;
	//scanf("%d", &T);
	while (T -- ) {
		solve();
	} 
	return 0;
}
posted @ 2025-03-18 02:01  maburb  阅读(717)  评论(7)    收藏  举报