VP Educational Codeforces Round 18


A. New Bus Route

题意:给你\(n\)个数,其差的绝对值最小的数对就有几个。

答案一定在排序后相邻的两个数里。

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

	std::sort(a.begin(), a.end());
	int ans = 2e9, cnt = 0;
	for (int i = 1; i < n; ++ i) {
		if (a[i] - a[i - 1] < ans) {
			ans = a[i] - a[i - 1];
			cnt = 1;
		} else if (a[i] - a[i - 1] == ans) {
			++ cnt;
		}
	}

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

B. Counting-out Rhyme

题意:每次以一个人为起点,在环里走\(a_i\)下把数到的那个人删掉,然后下一个人变成起点。求被删掉的人。

模拟题,\(a_i\)大于等于\(n\)的时候取模就行了,因为每走\(n\)次就走回来了。

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

    std::vector<int> b(n);
    std::iota(b.begin(), b.end(), 0);
    for (int i = 0; i < k; ++ i, -- n) {
    	int p = a[i] % n;
    	std::cout << b[p] + 1 << " \n"[i == k - 1];
    	std::vector<int> c(b.begin() + p + 1, b.end());
    	for (int j = 0; j < p; ++ j) {
    		c.push_back(b[j]);
    	}
    	b = c;
    }
}

C. Divide by Three

题意:给你一个大数,你要删去一些数使得这个数没有前导零且是3的倍数。

3的倍数就是每一位加起来是3的倍数。
那么我们考虑\(dp\)\(f[i][j]\)表示到\(i\)位模\(3\)等于\(j\)最少要删掉几个。转移就是枚举这个数要不要删。注意如果这一位是0且转移过来的把前面都删了就不要转移。

点击查看代码
void solve() {
    std::string s;
    std::cin >> s;
    int n = s.size();
    const int inf = 1e9;
    std::vector f(n + 1, std::vector<int>(3, inf));
    std::vector pre(n + 1, std::vector<int>(3, -1));
    f[0][0] = 0;

    auto change = [&](int i, int j, int x, int y, int v) -> void {
    	if (f[i][j] + v < f[x][y]) {
    		f[x][y] = f[i][j] + v;
    		pre[x][y] = j;
    	}
    };	

    for (int i = 0; i < n; ++ i) {
    	int d = s[i] - '0';
    	for (int j = 0; j < 3; ++ j) {
    		change(i, j, i + 1, j, 1);
    		if (d == 0 && f[i][j] == i) {
    			continue;
    		}
    		change(i, j, i + 1, (j + d) % 3, 0);
    	}
    }

    if (f[n][0] >= n) {
    	if (s.find('0') != s.npos) {
    		std::cout << "0\n";
    	} else {
	    	std::cout << -1 << "\n";
    	}
    	return;
    }

    std::string ans;
    for (int i = n, j = 0; i ; -- i) {
    	if (f[i][j] == f[i - 1][pre[i][j]]) {
    		ans += s[i - 1];
    	}

    	j = pre[i][j];
    }

    while (ans.size() > 1 && ans.back() == '0') {
    	ans.pop_back();
    }

    if (ans.empty()) {
    	std::cout << -1 << "\n";
    	return;
    }

    std::reverse(ans.begin(), ans.end());
	std::cout << ans << "\n";
}

D. Paths in a Complete Binary Tree

题意:一棵满二叉树,每个节点的编号就是这个点在中序遍历时的时间戳。给出一个操作序列,从某个位置往上往左往右走,求最后到哪个点。

找规律题。首先发现每一层数的编号是一个等差序列,第\(i\)层最小的点是\(2^{n-i}\),公差为\(2^{n-i+1}\),其中\(n\)是总层数。那么这一层的数只需要求自己的\(lowbit\)就能求出这一层最小的数,因为这一层的数是若干个\(2^{n-i+1}\)加上一个\(2^{n-i}\),容易发现\(n-i\)位一定是1。然后上下就可以根据层来讨论,先看是左节点还是右节点,左节点往上是加\(2^{n-i-1}\),右节点往上是减\(2^{n-i-1}\),往下讨论同理。于是模拟就行。

点击查看代码
void solve() {
    i64 n, q;
    std::cin >> n >> q;
    while (q -- ) {
    	i64 x;
    	std::cin >> x;
    	i64 y = x & -x;
    	std::string s;
    	std::cin >> s;
    	for (auto & c : s) {
    		if (c == 'U') {
    			if (y * 2 == n + 1) {
    				continue;
    			} else {
    				if ((x - y) / (y * 2) % 2 == 0) {
    					x += y;
    				} else {
    					x -= y;
    				}

    				y *= 2;
    			}
    		} else if (c == 'L') {
    			if (x & 1) {
    				continue;
    			} else {
    				x -= y / 2;
    				y /= 2;
    			}
    		} else {
    			if (x & 1) {
    				continue;
    			} else {
    				x += y / 2;
    				y /= 2;
    			}
    		}
    	}

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

E. Colored Balls

题意:\(n\)个数,你要选一个\(x\)把每个数分成若干个\(x\)\(x+1\),求所有数分成的部分总数最少。

如果一个\(x\)是合法的,则对于每个\(i\)都满足\(a_i \% x \leq \lfloor \frac{a_i}{x} \rfloor\),这表示我把\(a_i\)\(x\)个分组,然后多出来\(a_i \% x\)个,这些就要放到\(x\)里变成\(x+1\),所以多出来的不能大于\(x\)的个数。
假设\(a_1\)是最小的,那么\(x\)肯定小于等于\(a_1\),那么我们可以枚举\(a_1\)被分成了几组,然后检查合不合法,那么如果\(a_i\)分成\(k\),则有\(x = \lfloor \frac{a_i}{k} \rfloor\),检查是不是合法的,然后更新答案就行。

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

    std::sort(a.begin(), a.end());

    const i64 inf = 1e18;
    auto check = [&](int k) -> i64 {
    	if (k <= 0) {
    		return inf;
    	}
    	i64 sum = 0;
    	for (int i = 0; i < n; ++ i) {
    		if (a[i] % k > a[i] / k) {
    			return inf;
    		}
    		sum += (a[i] + k) / (k + 1);
    	}

    	return sum;
    };

    i64 ans = inf;
    for (int i = 1; i <= a[0] / i; ++ i) {
    	ans = std::min({ans, check(i), check(i - 1)});
    	ans = std::min({ans, check(a[0] / i),  check(a[0] / i - 1)});
    }

    std::cout << ans << "\n";
}
posted @ 2025-02-28 16:37  maburb  阅读(18)  评论(0)    收藏  举报