Codeforces Round 946 (Div. 3) 题解
Codeforces Round 946 (Div. 3) 题解
A. Phone Desktop 贪心
优先考虑放 \(2\times2\) 的,然后剩下的补 \(1\times 1\) 的。注意需要上取整即可。
#include<bits/stdc++.h>
using namespace std;
#define ff first
#define ss second
#define pb push_back
#define all(u) u.begin(), u.end()
#define endl '\n'
#define debug(x) cout<<#x<<":"<<x<<endl;
typedef pair<int, int> PII;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 10, M = 105;
const int mod = 1e9 + 7;
const int cases = 1;
void Showball() {
int x, y;
cin >> x >> y;
int t = y / 2;
x -= t * 7;
if (y & 1) x -= 11, t++;
cout << t + (max(0, x) + 14) / 15 << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T = 1;
if (cases) cin >> T;
while (T--)
Showball();
return 0;
}
B. Symmetric Encoding 模拟
解密和加密过程一样,直接模拟即可。
void Showball() {
string s;
cin >> s >> s;
set<char> st;
for (auto c : s) st.insert(c);
string t = "";
for (auto c : st) t += c;
int n = t.size();
map<char, char> mp;
for (int i = 0; i < n; i++) {
mp[t[i]] = t[n - i - 1];
}
for (auto c : s) cout << mp[c];
cout << endl;
}
C. Beautiful Triple Pairs 计数
题意: 找出所有满足有一个位置上完全不同,其余两个位置相同的三元组对数。
思路: 根据不同元素的位置,可以分成三种情况,为了避免重复,我们可以维护出两个数组。
\(mp_{i,j,k}\) 数组表示三元组 \((i,j,k)\) 的数量。 $s_{i,j} $ 数组表示三元组 \((x,i,j)\) 的数量。
那么我们统计第一位不同的情况的答案时,就可以枚举所有的三元组, 每个三元组的贡献就为
\(mp_{i,j,k}\times (s_{i,j}-mp_{i,j,k})\) 。 表示第一位为 \(i\) 的和第一位不为 \(i\) 的都可以两两构成满足条件的三元组对。
其余两种情况同理。这两个数组用 \(map\) 维护一下即可。
最后答案记得除以 \(2\) ,因为同一种情况会计算两遍。
代码:
void Showball(){
int n;
cin>>n;
vector<int> a(n);
for(int i=0;i<n;i++) cin>>a[i];
map<array<int,3>,int> mp,mp2,mp3;
map<PII,int> s,s2,s3;
for(int i=0;i<n-2;i++){
mp[{a[i+1],a[i+2],a[i]}]++;
mp2[{a[i],a[i+2],a[i+1]}]++;
mp3[{a[i],a[i+1],a[i+2]}]++;
s[{a[i+1],a[i+2]}]++;
s2[{a[i],a[i+2]}]++;
s3[{a[i],a[i+1]}]++;
}
LL res=0;
for(auto [k,v]:mp) res+=v*(s[{k[0],k[1]}]-v);
for(auto [k,v]:mp2) res+=v*(s2[{k[0],k[1]}]-v);
for(auto [k,v]:mp3) res+=v*(s3[{k[0],k[1]}]-v);
cout<<res/2<<endl;
}
D. Ingenuity-2 思维
题意:给你方向指令,初始两人在同一点,请你分配两人去按照指令行走,每人必须至少走一步。使得两人走完指令后最终到达同一点。
思路:由于初始两人在同一点,那么我们想要保证两人最终在同一点,首先,相反方向可以让一个人走,就可以相互抵消,对于没有抵消的部分,如果是偶数次,那么就可以两人一起走,反之,则无解。所以直接判断相反方向的奇偶性是否相同即可。
注意一些特判,n=2时,必须两条指令相同,因为每人至少走一步。对于 n=4,并且每个方向走出现一次的情况。我们也需要分别让每个人走一对相反的方向即可。
代码:
void Showball() {
int n;
cin >> n;
string s;
cin >> s;
map<char, int> mp;
for (auto c : s) mp[c]++;
if (n == 2 && s[0] != s[1]) return cout << "NO\n", void();
if (n == 4 && mp['N'] && mp['S'] && mp['E'] && mp['W']) {
for (auto c : s) {
if (c == 'N' || c == 'S') cout << "R";
else cout << "H";
}
cout << endl;
return;
}
if ((mp['N'] & 1) != (mp['S'] & 1) || (mp['E'] & 1) != (mp['W'] & 1)) return cout << "NO\n", void();
map<char, int> mp2;
for (auto c : s) {
mp2[c]++;
if (mp2[c] == mp[c]) {
cout << (mp2[c] & 1 ? "R" : "H");
} else {
cout << (mp2[c] & 1 ? "R" : "H");
}
}
cout << endl;
}
E. Money Buys Happiness 思维 DP
题意:给你 \(m\) 个月, 每个月可以赚 \(x\) 元, 每个月你都有一次机会花费 \(c_i\) 元, 获得 \(h_i\) 的幸福。(当然你目前得有足够的钱)。 求出能够获得的最大幸福值。
注意: \(1 \le m \le 50\) 。
思路:直接贪心显然是不对的,考虑DP,我们可以求出获得 \(i\) 幸福值所需的最小花费,然后判断能否有足够的钱即可。考虑如何求解, 把花费 \(c_i\) 看成物品价值,把 \(h_i\) 看成物品体积。那么容易发现,这个问题是一个 \(01\) 背包问题。那么状态转移就可以得到 :\(f_j = min(f_j, f_{j-h_i}+w_i)\)。
代码:
void Showball() {
LL m, x;
cin >> m >> x;
vector<LL> c(m + 1), h(m + 1);
int sum = 0;
for (int i = 1; i <= m; i++) cin >> c[i] >> h[i], sum += h[i];
vector<LL> f(sum + 1, 1e18);
f[0] = 0;
for (int i = 1; i <= m; i++) {
for (int j = sum; j >= h[i]; j--) {
if (f[j - h[i]] + c[i] <= (i - 1)*x)
f[j] = min(f[j], f[j - h[i]] + c[i]);
}
}
for (int i = sum; i >= 0; i--) {
if (f[i] <= (m - 1)*x) {
return cout << i << endl, void();
}
}
}
F. Cutting Game 思维
题意:Alice 和 Bob 玩游戏,有一个大小为 \(a\times b (1\le a,b \le 10^9)\) 的网格, 网格上有 \(n\) 个筹码,坐标为 \((x_i,y_i)\)。接下来两人开始轮流操作。一共有四种操作:
\('U'\) \(k\) : 删除剩余的前 \(k\) 行。
\('D'\) \(k\) : 删除剩余的后 \(k\) 行。
\('L'\) \(k\) : 删除剩余的前 \(k\) 列。
\('R'\) \(k\) : 删除剩余的后 \(k\) 列。
每次操作后,该选手就会获得删除部分存在的所有筹码数量的得分。
求出操作结束后,两名选手各自的分数。
思路:因为删除的行或者列会永远消失,那么我们可以维护好剩余矩形的边界 \(rl,rr,cl,cr\)。 以第一种操作为例:
注意到矩阵的范围很大,但是每个点比较离散,那么可以把所有的点存进一个队列,并且维护好每一行内部的顺序,这样需要 \(nlogn\) 的时间复杂度。这样每次统计完就可以把该行从队列中弹出去即可。由于需要操作头和尾,所以开双端队列即可。对于每一行内部,我们已经排好了序,那么只需要二分出左右端点的位置,即可算出个数。
小tips:因为我们需要存每一行,每一列都有哪些点。如果开二维数组的话无疑会爆。所以我们可以开一个 \(map\) 套 \(vector\) 即可。
代码:
void Showball() {
int a, b, n, m;
cin >> a >> b >> n >> m;
map<int, vector<int>> row, col;
for (int i = 1; i <= n; i++) {
int x, y;
cin >> x >> y;
row[x].pb(y);
col[y].pb(x);
}
deque<int> rows, cols;
int rl = 1, rr = a, cl = 1, cr = b;
for (auto &p : row) {
rows.pb(p.ff);
sort(all(p.ss));
}
for (auto &p : col) {
cols.pb(p.ff);
sort(all(p.ss));
}
auto calc = [&](vector<int> vec, int l, int r) {
int ret = 0;
if (l > r) return ret;
auto L = lower_bound(all(vec), l);
auto R = upper_bound(all(vec), r);
ret = R - L;
return ret;
};
int Alice = 0, Bob = 0;
for (int i = 0; i < m; i++) {
char op;
int k;
cin >> op >> k;
int cur = 0;
if (op == 'U') {
while (!rows.empty() && rows.front() < rl + k) {
int x = rows.front(); rows.pop_front();
cur += calc(row[x], cl, cr);
}
rl += k;
} else if (op == 'D') {
while (!rows.empty() && rows.back() > rr - k) {
int x = rows.back(); rows.pop_back();
cur += calc(row[x], cl, cr);
}
rr -= k;
} else if (op == 'L') {
while (!cols.empty() && cols.front() < cl + k) {
int x = cols.front(); cols.pop_front();
cur += calc(col[x], rl, rr);
}
cl += k;
} else {
while (!cols.empty() && cols.back() > cr - k) {
int x = cols.back(); cols.pop_back();
cur += calc(col[x], rl, rr);
}
cr -= k;
}
if (i & 1) Bob += cur;
else Alice += cur;
}
cout << Alice << " " << Bob << endl;
}
G. Money Buys Less Happiness Now 反悔贪心
题意:给你 \(m\) 个月, 每个月可以赚 \(x\) 元, 每个月你都有一次机会花费 \(c_i\) 元, 获得 \(1\) 单位的幸福。(当然你目前得有足够的钱)。 求出能够获得的最大幸福值。
注意: \(1 \le m \le 2·10^5\) 。
思路:相比 \(E\) 题, \(m\) 的范围变大了,不能用同样的方式处理了,但是每次都是获取 \(1\) 单位的幸福。考虑反悔贪心。记录当前剩余的钱为 \(sum\) , 每次 \(sum -=c_i\) ,并且将 \(c_i\) 放入优先队列中,如果 \(sum < 0\) ,说明需要反悔。自然是选 \(c_i\) 最大的进行反悔。最后队列元素个数就是答案。每次记得 \(sum += x\)。
代码:
void Showball(){
int m, x;
cin >> m >> x;
vector<int> c(m);
for (int i = 0; i < m; i++) {
cin >> c[i];
}
int sum = 0;
priority_queue<int> pq;
for (int i = 0; i < m; i++) {
sum -= c[i];
pq.push(c[i]);
if (sum < 0) sum += pq.top(), pq.pop();
sum += x;
}
cout<< pq.size() << endl;
}