题目链接:Educational Codeforces Round 172 (Rated for Div. 2)
A题 贪心(题目中也明确规定了就是贪心),排序从最大的开始取就行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void solve () { int n, k; cin >> n >> k; std::vector<int > a (n) ; for (int i = 0 ; i < n; i++) { cin >> a[i]; } std::sort (a.begin (), a.end (), std::greater <int >()); int res = 0 ; for (int i = 0 ; i < n; i++) { if (res + a[i] <= k) { res += a[i]; } else break ; } cout << k - res << '\n' ; }
B题 贪心,如果有数字只出现了一次,那么取到它可以得到两分,设该数字个数为$cnt1$,则先手可以得到$2\lceil \frac{cnt1}{2} \rceil$分,之后出现多次的数字一定不可能被一个人取完,但一定可以取到一个,设出现次数大于一的数字个数为$cnt2$ ,那么最终答案就是$2\lceil \frac{cnt1}{2} \rceil+cnt2$。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void solve () { int n; cin >> n; std::vector<int > cnt (n + 1 ) ; for (int i = 0 ; i < n; i++) { int num; cin >> num; cnt[num]++; } int cnt1 = 0 , cnt2 = 0 ; for (int i = 1 ; i <= n; i++) { if (cnt[i] == 1 ) { cnt1++; } else if (cnt[i] > 1 ) { cnt2++; } } cout << (cnt1 + 1 ) / 2 * 2 + cnt2 << '\n' ; }
C题 换一个角度看这个问题,我们将整个位串分为$m$组,每一组的1的个数与0的个数之差乘该组权值就是该组Bob超过Alice的分数,即$\sum_{i=1}^{m}(i-1)(cnt1_i-cnt0_i)=\sum_{i=2}^{m}(\sum_{j=i}^{m}cnt1_i-\sum_{j=i}^{m}cnt0_i)$,也就是从第二组开始到最后位置的所有1,0的数量差+从第三组开始到最后位置所有1,0的数量差…,这样算下来,第二组被加了一次,第三组被加了两次,…,第m组只被加了(m-1)次,这样我们只需要看从某一个位置作为起点到最后一个位置的0,1数量差,把它当作一组。
从后往前推出从当前位置到末尾的10数量差,之后从大到小取即可,注意第一组没有加权,排序时应丢弃第一个位置的数字。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 void solve () { int n, k; cin >> n >> k; string s; cin >> s; std::vector<int > sum (n) ; for (int i = n - 1 ; i >= 0 ; i--) { if (i < n - 1 ) { sum[i] = sum[i + 1 ]; } if (s[i] == '1' ) { sum[i]++; } else { sum[i]--; } } sum.erase (sum.begin ()); std::sort (sum.begin (), sum.end (), std::greater ()); i64 ans = 0 ; for (int i = 1 ; i < n; i++) { ans += sum[i - 1 ]; if (ans >= k) { cout << i + 1 << '\n' ; return ; } } cout << -1 << '\n' ; }
D题 (来源:jiangly)
我们先考虑吧左边界的情况,将所有用户按左边界按从小到大排序,那么左侧的用户的左边界一定比当前序号的用户左边界小,右侧的用户左边界一定比当前用户大,那么右侧的用户就不可能作为当前用户的predictor ,此时取左侧所有用户右边界的大于当前序号的右边界的最小值,作为向当前用户推荐音乐的右边界,反之左边界同理。
注意如果有两个或多个序号左右边界相同,那么他们互为predictor ,这需要特判一下,因为它们之中的第一个在确定推荐音乐的边界不知道第二个的存在,因此需要向下看一位,如果是边界相同的话,再更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 void solve () { int n; cin >> n; std::vector<int > l (n) , r (n) ; for (int i = 0 ; i < n; i++) { cin >> l[i] >> r[i]; } std::vector<int > L (n, -1 ) , R (n, -1 ) ; std::vector<int > p (n) ; std::iota (p.begin (), p.end (), 0 ); { std::sort (p.begin (), p.end (), [&](int i, int j) { if (l[i] != l[j]) { return l[i] < l[j]; } return r[i] > r[j]; }); std::set<int > ms; for (int j = 0 ; j < n; j++) { int i = p[j]; auto it = ms.lower_bound (r[i]); if (it != ms.end ()) { R[i] = *it; } ms.insert (r[i]); if (j < n - 1 && l[i] == l[p[j + 1 ]] && r[i] == r[p[j + 1 ]]) { R[i] = r[i]; } } } { std::sort (p.begin (), p.end (), [&](int i, int j) { if (r[i] != r[j]) { return r[i] > r[j]; } return l[i] < l[j]; }); std::set<int > ms; for (int j = 0 ; j < n; j++) { int i = p[j]; auto it = ms.upper_bound (l[i]); if (it != ms.begin ()) { L[i] = *std::prev (it); } ms.insert (l[i]); if (j < n - 1 && l[i] == l[p[j + 1 ]] && r[i] == r[p[j + 1 ]]) { L[i] = l[i]; } } } for (int i = 0 ; i < n; i++) { int ans; if (L[i] == -1 ) { ans = 0 ; } else { ans = R[i] - L[i] - (r[i] - l[i]); } cout << ans << '\n' ; } }