【CF】Day51——Codeforces Round 963 (Div. 2) CD
C. Light Switches
题目:
思路:
思维+数学
我们首先肯定能找到一个规律,那就是开灯的时间戳是 a[i] + 2*k*x (x >= 0),且这是规律性的,那么最后的答案一定是在 max(a[i]) ~ max(a[i]) + k - 1 之间的,因为后面最晚亮的灯熄灭了,那么就要重新开始循环了,比如假设答案是 m,那么之后的答案也是 m + 2*k
那我们如何写呢?我们既然知道答案在其中,那么我们其实就可以将所有的 a[i] 全投影到 max(a[i]) ~ max(a[i]) + k - 1 之间,然后再排序一遍,如果最大值与最小值之差小于等于 k,那么就有答案,否则无解
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "Yes\n"
#define no cout << "No\n"void solve()
{int n, k;cin >> n >> k;vector<int> a(n+1);for (int i = 1; i <= n; i++){cin >> a[i];}sort(a.begin() + 1, a.end());int MOD = 2 * k;for (int i = 1; i <= n; i++){a[i] += (a[n] + k - 1 - a[i]) / MOD * MOD;}sort(a.begin() + 1, a.end());if (a[n] - a[1] + 1 > k){cout << "-1\n";return;}cout << a[n] << endl;
}signed main()
{cin.tie(0)->sync_with_stdio(false);int t = 1;cin >> t;while (t--){solve();}return 0;
}
D. Med-imize
题目:
思路:
经典中位数,还有似曾相识的二分+dp
首先我们要知道如何二分快速求中位数,我们假设中位数是 k,那我们可以令 a[i] >= k 的权值为 -1,而 a[i] < k 的权值为 1,那么最后只要权值之和 sum > 0 说明 k 是可以的
而上面这种方法的内核其实就是转换我们要求的东西,本题就是这种方式,我们可以二分最后的结果 mid,然后再判断是否符合,但是我们如何判断呢?
由于本体让我们要执行操作后的数组的长度小于等于 k,且我们每次都要删除连续 k 个数,那我们观察一下是否有什么规律?
我们可以将所有的下标模上k,我们可以发现我们每次删连续k个数都是删掉下标为 0 ~ k 的一个连续段,也就是说删掉后我们剩下的数组还是连续的,从结果来看,最后剩下的数组的下标肯定是 0 1 2 3 4 ... k (可能比k小),那这有什么用呢?
假如我们要保留第 1 个 0,那么下一个保留的 1 一定是在这个 0 的后面的,2 3 4...同理,也就是说我们可以用前一个数来推出后面的结果,那么我们就可以使用dp来解决这个问题
我们定义 dp[i] 为前 i 个元素中已经将数组删除到长度小于等于 k 时的权值之和,那么对于 dp[i] 我们有两种情况
如果 i % k == 0,也就是说这个点是 0,那我们就不能从上一个点直接转移过来,那我们就要新增一种保留的 0 的位置的情况,或者不新增(那就是继承之前的情况,由于是0,那么就要从0处转移过来),那么转移方程就是 dp[i] = max(dp[i - k],val)
否则对于不是 0 的元素,我们可以从前面这个元素直接转移过来,即dp[i] = dp[i - 1] + val,或者我们从之前的那个元素转移过来,即 dp[i] = d[i - k],二者取max即可,同时要注意特判 i > k
上诉dp转移最后的结果就是我们最后选择了下标为 0 ~ k(可能没有 k) 的元素各一个,然后其奉献和的值,判断也好判断了,即 dp[n - 1] > 0,最后二分经典操作即可
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <memory>
using namespace std;
#define int long long
#define yes cout << "Yes\n"
#define no cout << "No\n"void solve()
{int n, k;cin >> n >> k;vector<int> a(n);for (int i = 0; i < n; i++){cin >> a[i];}auto check = [&](int mid) {vector<int> dp(n, 0);dp[0] = (a[0] >= mid ? 1 : -1);for (int i = 1; i < n; i++){if (i % k == 0){dp[i] = max(dp[i - k], (a[i] >= mid ? 1LL : -1LL));}else{dp[i] = dp[i - 1] + (a[i] >= mid ? 1LL : -1LL);if (i > k){dp[i] = max(dp[i], dp[i - k]);}}}return dp[n-1] > 0;};int l = 1, r = 1e9;while (l + 1 < r){int mid = l + r >> 1;if (check(mid)){l = mid;}else{r = mid;}}if (check(r)){cout << r << endl;return;}cout << l << endl;
}signed main()
{cin.tie(0)->sync_with_stdio(false);int t = 1;cin >> t;while (t--){solve();}return 0;
}