Educational Codeforces Round 9 题解
A
比较阅读理解。。读懂了还好
int n, p;
std::vector<std::string> vs;
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> p;
rep (i, 1, n) {
std::string s; cin >> s;
vs.push_back(s);
}
std::reverse(ALL(vs));
lli apples = 0;
lli ans = 0;
for (auto s : vs) {
if (s.size() == 8) {
// halfplus
lli last_apples = apples * 2 + 1;
ans += apples * p + (p / 2);
apples = last_apples;
} else {
// half
lli last_apples = apples * 2;
ans += apples * p;
apples = last_apples;
}
}
printf("%lld\n", ans);
return 0;
}
B
枚举翻转哪个位置的前缀,用前缀和算一算就行了。后缀同理。
const int MAXN = 5e5 + 10;
int n;
lli pp[MAXN];
char ss[MAXN];
int seq[MAXN];
lli pref[MAXN][2];
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n;
rep (i, 1, n) cin >> pp[i];
cin >> ss;
rep (i, 0, n - 1) seq[i + 1] = (ss[i] == 'B');
lli ans = 0;
rep (cas, 0, 1) {
rep (i, 1, n) rep (j, 0, 1) pref[i][j] = pref[i - 1][j] + (seq[i] == j) * pp[i];
ans = std::max(ans, pref[n][1]);
rep (i, 1, n) {
lli allans = pref[n][1];
allans -= pref[i][1]; allans += pref[i][0];
ans = std::max(ans, allans);
}
std::reverse(seq + 1, seq + 1 + n);
std::reverse(pp + 1, pp + 1 + n);
}
printf("%lld\n", ans);
return 0;
}
C
经典贪心。
const int MAXN = 5e4 + 10;
int n;
std::string ss[MAXN];
bool cmp(std::string sa, std::string sb) {
return sa + sb < sb + sa;
}
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n;
rep (i, 1, n) cin >> ss[i];
std::sort(ss + 1, ss + 1 + n, cmp);
rep (i, 1, n) cout << ss[i];
cout << endl;
return 0;
}
D. Longest Subsequence
基础数论题,然而我不会。
首先 \(> m\) 的数都可以扔了,这样我们可以把数据范围缩到 \(10^6\)。
接下来考虑算贡献:记录每个数字能对哪些 lcm 产生贡献,也就是对于 \(i \in [1, m]\),如果 \(x|i\) 那么让 \(\mathrm{ans}_i\) 加 \(1\)。这个东西可以用一个类似埃氏筛的东西优化,即对于一个 \(x\),让所有 \(\mathrm{ans}_{kx}(kx \leq m)\) 都加 \(1\)。重复的数字可以合并计算。
时间复杂度 \(O(m\log m)\),虽然这个我也不知道咋来的。
最开始没看到输出下标,然后看了好一会也没看出来样例为啥是对的。。。
#易错警示:题目要输出什么一定好好看清。
const int MAXN = 1e6 + 10;
int n, m;
std::vector<int> bwl[MAXN];
int occ[MAXN];
std::vector<int> nums;
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
n = read(); m = read();
rep (i, 1, n) {
int x = read();
if (x > m) continue;
bwl[x].push_back(i);
}
rep (i, 1, MAXN - 5) if (bwl[i].size()) nums.push_back(i);
for (auto v : nums) {
for (int j = 1; j * v <= m; ++j) {
occ[j * v] += bwl[v].size();
}
}
int tans = 1;
for (int i = 1; i <= m; ++i) if (occ[i] > occ[tans]) tans = i;
cout << tans << ' ' << occ[tans] << endl;
std::vector<int> inds;
for (auto v : nums) if (tans % v == 0) for (auto id : bwl[v]) inds.push_back(id);
std::sort(ALL(inds));
for (auto id : inds) cout << id << ' ';
cout << endl;
return 0;
}
E. Thief in a Shop
一个很 nb 的做法。
正着 DP 不好写考虑交换维度和答案:设 f[i][j]
表示考虑前 i 个物品,凑出价格为 j 最少需要多少个物品,第一维可以滚掉。一个很普通的完全背包。
然而这么写连样例都过不去。
问题在于,题目让我们求的是恰好 \(k\) 个物品的情况,但是这么 DP 只能算出 \(\leq k\) 个物品的情况。如果有价格为 \(0\) 的物品的话,不足 \(k\) 个物品也能通过 \(0\) 价格物品来补齐,但是这里没有 \(0\)。
于是考虑造出一个 \(0\) 来。
取 \(v = \min a_i\),接下来把所有物品的价格都减掉 \(v\),此时我们就成功获得了一个 \(0\) 价格物品!对修改过价格的物品做背包,我们可以知道每一种(修改后的)价格最少需要多少物品凑出来,如果 \(\leq k\) 就说明这个(修改后的)价格是合法的。
注意到所有物品的价格都被减掉了 \(v\),那么选 \(k\) 个物品时价格就被减掉了 \(kv\),所以输出答案的时候还要将价格加上 \(kv\)。
const int MAXN = 1000 + 10;
int n, k;
int aa[MAXN];
int dp[MAXN * MAXN];
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
n = read(); k = read();
rep (i, 1, n) aa[i] = read();
std::sort(aa + 1, aa + 1 + n);
rep (i, 2, n) aa[i] -= aa[1];
memset(dp, 0x3f, sizeof dp);
dp[0] = 0;
rep (i, 2, n) {
rep (j, aa[i], aa[n] * k) {
dp[j] = std::min(dp[j], dp[j - aa[i]] + 1);
}
}
rep (i, 0, aa[n] * k) {
if (dp[i] <= k) {
cout << i + aa[1] * k << ' ';
}
}
cout << endl;
return 0;
}
F. Magic Matrix
首先可以把题目转化成对于每个点 \((i, j)\),求第 \(i\) 行和第 \(j\) 行每列元素对应取 max 是否都大于 \((i, j)\)。
考虑确定当前值后将其他元素变为“大于为 1 小于为 0”这样一个思想,我们对于每一个节点 \((i, j)\),将所有大于等于它的数字变成 \(0\),小于它的数字变成 \(1\),那么题设就转化成了第 \(i\) 行和第 \(j\) 行按位与起来是否不为 0.
接下来再用边插入边查询的思想,按权值从小到大依次考虑所有节点,将已经考虑过的节点设置为 1(一起处理所有相同权值的节点),对当前节点查询第 \(i\) 行和第 \(j\) 行按位与起来是否不为 0 即可。这个过程可以用 bitset
优化,时间复杂度 \(O(n^3/64)\).
const int MAXN = 2500 + 10;
int n;
int aa[MAXN][MAXN];
std::bitset<MAXN> mat[MAXN];
bool checksym() {
for (int i = 1; i <= n; ++i) if (aa[i][i]) return false;
rep (i, 1, n) rep (j, 1, n) if (aa[i][j] != aa[j][i]) return false;
return true;
}
struct Node {
int i, j, x;
} nodes[MAXN * MAXN]; int cnt;
bool cmp(Node x, Node y) { return x.x < y.x; }
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
n = read();
rep (i, 1, n) rep (j, 1, n) aa[i][j] = read();
if (!checksym()) {
cout << "NOT MAGIC" << endl;
return 0;
}
rep (i, 1, n) rep (j, 1, n) {
nodes[++cnt] = {i, j, aa[i][j]};
}
std::sort(nodes + 1, nodes + 1 + cnt, cmp);
for (int i = 1; i <= cnt; ++i) {
int r = i;
while (r + 1 <= cnt && nodes[i].x == nodes[r + 1].x) ++r;
for (int x = i; x <= r; ++x) {
int a = nodes[x].i, b = nodes[x].j;
if ((mat[a] & mat[b]).any()) {
// mat[i][j] = 0 if AA[i][j] > AA[a][b]
cout << "NOT MAGIC" << endl;
return 0;
}
}
for (int x = i; x <= r; ++x) mat[nodes[x].i][nodes[x].j] = 1;
i = r;
}
cout << "MAGIC" << endl;
return 0;
}