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;
}

posted @ 2021-10-28 17:17  Handwer  阅读(24)  评论(0编辑  收藏  举报