c题普遍不考察高深算法,而考察对问题的分析能力和简单的数学推导。从最近的比赛来看,我对于c题总是解决得很慢或者根本无法解决。于是准备狂刷50道div2 c题,记录一下。
1.Codeforces Round 921 (Div. 2) C
考察点:贪心,构造反例
发现k个字母能form的长度为n的串数量是非常多的,如果去检验每一个串是否在s中是否出现复杂度过大且代码不好实现。这种情况下可以考虑从反方向思考,去找哪种串最不“容易”出现呢?也就是构造反例。对于构造的反例的第一位,使用k个字母中在s中第一次出现位置下标最大的那个一定是最优的。这个字母加入到反例中,将s的相应前缀删除,重复上述过程。如果最后构造出的反例长度大于等于n,那么输出YES;否则输出NO,一个没有出现的子串可以构造为当前构造出的反例加上若干个在剩下的s中没有出现的字母,将反例长度扩充为n输出即可。
2. Codeforces Round 922 (Div. 2) C
考察点:贪心,位运算
按位考虑,不妨设a>=b,若a=b,则答案为0;若a>b,则一定存在最高位,此位上a为1,b为0。那么想要题目所问的绝对值最小,只需要把接下来a为1,b为0的位异或1即可。(想想看怎么证明)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define For(i, a, b) for (int i = (a); i <= (b); i++)
#define FOR(i, a, b) for (int i = (a); i >= (b); i--)
int main() {
int tc; scanf("%d", &tc);
while (tc--) {
ll a, b, r;
cin >> a >> b >> r;
if (a < b) swap(a, b);
ll a1 = a, b1 = b;
int n1 = 0, n2 = 0, c1[70] = {0}, c2[70] = {0};
while (a) {
c1[n1++] = a & 1;
a >>= 1;
}
while (b) {
c2[n2++] = b & 1;
b >>= 1;
}
ll ans = 0;
bool flag = false;
for (int i = max(n1, n2) - 1; i >= 0; i--) {
if (c1[i] == 1 && c2[i] == 0 && flag) {
if (ans + (1ll << i) <= r) ans += (1ll << i);
}
if (c1[i] == 1 && c2[i] == 0 && !flag) {
flag = true;
}
}
ll res = abs((a1 ^ ans) - (b1 ^ ans));
cout << res << endl;
}
return 0;
}
3.Codeforces Round 922 (Div. 2) D
考察点:二分,单调队列优化dp
简要题意:给定数组a,现在在a数组中删去若干个数,将a数组划分成若干段,记当前删除方法的价值为max{删去数字的和,max{每段之和}},求所有删除方法中价值最小是多少?
不难想到二分答案,对于每次二分过程中的check,考虑dp。
dp[i]表示已经划分好1~i中所有序列,且第i个元素被删除的所有方案中删去数字之和的最小值。
转移方程为dp[i] = min{dp[j]} + a[i] 其中,且(j + 1) ~ (i - 1)中数字之和满足check条件。寻找min{dp[j]}可以利用类似滑动窗口的思想,用单调队列来维护。
算法复杂度.