AtCoder Beginner Contest 354 (PARTIAL) Editorial

AtCoder Beginner Contest 354 (PARTIAL) Editorial

0. 前言

这应该算是我第一次写整场比赛的题解。

然而,由于我不会这场比赛的 D 和 G 题,故没有这两题的题解。

1. Exponential Plant - 指数植物

题意

一棵植物在第 \(i\) 天的高度为 \((2^i-1)\text{cm}\),问最早在第几天植物高度高于指定高度 \(H\text{cm}\)

思路

直接模拟即可。

代码

#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int t, n, r = 1, i = 0;
int main()
{
    cin >> n;
    while (t <= n)
        t += r, r <<= 1, i++;
    printf("%d\n", i);
}

2. AtCoder Janken 2 - 石头剪刀布

题意

给定 \(n\) 个字符串 \(s_i\)\(n\) 个整数 \(c_i\),求 \(s\) 中字典序第 \(\sum_{i=1}^n c_i\bmod n+1\) 小的字符串。

思路

直接模拟即可。

代码

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
const int N = 1e2 + 10;
int sm, n;
string s[N];
int main()
{
    cin >> n;
    for (int i = 0, x; i < n; i++)
    {
        cin >> s[i] >> x;
        sm += x;
    }
    sort(s, s + n);
    cout << s[sm % n] << '\n';
}

3. AtCoder Magics - 魔术

题意

给定 \(n\) 个整数 \(a_i,c_i\),在集合 \(\{1,2,...,n\}\) 内删去所有存在整数 \(x\) 满足 \(a_x>a_y\)\(c_x<c_y\) 的整数 \(y\),求无法再次进行删除的集合。

思路

\((a_i,c_i)\) 绑定为 pair 并进行升序排序,之后进行暴力删除即可。

代码

#include <iostream>
#include <map>
#include <set>
#include <algorithm>
using namespace std;
const int N = 2e5 + 10;
map<int, set<int>, greater<int>> mp;
set<int> res;
int n;
using pii = tuple<int, int, int>;
pii a[N];
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        auto &[x, y, z] = a[i];
        scanf("%d%d", &x, &y);
        z = i;
    }
    sort(a + 1, a + n + 1);
    for (int i = 1; i <= n; i++)
    {
        while (mp.size() and mp.begin()->first > get<1>(a[i]))
        {
            mp.erase(mp.begin());
        }
        mp[get<1>(a[i])].insert(get<2>(a[i]));
    }
    for (auto &[k, v] : mp)
    {
        for (auto &j : v)
            res.insert(j);
    }
    printf("%d\n", res.size());
    for (auto &i : res)
        printf("%d ", i);
}

4. Remove Pairs - 卡牌配对

题意

高桥和青木的面前有 \(n\) 张卡牌,每张卡牌的正反面分别写着 \(a_i,b_i\) 。编号为 \(x,y\) 的卡牌可以被配对并移除,当且仅当满足 \(a_x=a_y\)\(b_x=b_y\) 。无法再次移除卡牌的人输。高桥先手。判断拥有必胜策略的是高桥还是青木。

思路

比 D 简单。

题目中的数据范围为 \(1\le n\le 18\),故解法为状压+博弈论。

使用一个整数中的 \(n\) 个比特位表示一种游戏状态,即有哪些牌可用。

显然,根据可用的牌,可以枚举出所有该状态的下一步状态。

在一个状态下先手拥有必胜策略,当且仅当该状态至少存在一个后继状态先手必败。

于是暴力枚举的实现是容易的。不过需要一些记忆化,否则有超时的风险。

代码

#include <iostream>
using namespace std;
const int N = 20;
int n, a[N], b[N];
bool bt[1 << N], vis[1 << N];
bool dfs(int mem)
{
    if (vis[mem])
        return bt[mem];
    vis[mem] = true;
    for (int i = 0; i < n; i++)
    {
        if (mem & (1 << i))
            continue;
        for (int j = i + 1; j < n; j++)
        {
            if (mem & (1 << j) or (a[i] != a[j] and b[i] != b[j]))
                continue;
            bt[mem] |= !dfs(mem | (1 << i) | (1 << j));
            if (bt[mem])
                return true;
        }
    }
    return false;
}
int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
    {
        scanf("%d%d", a + i, b + i);
    }
    puts(dfs(0) ? "Takahashi" : "Aoki");
}

5. Useless for LIS - 最长上升子序列并集

题意

给出 \(n\) 个整数 \(a_i\) ,求处于哪些下标上的 \(a_i\) 可以构成一个最长上升子序列。

思路

还是没有 D 难。

用树状数组求最长上升子序列长度显然是基操。

\(a_i\) 比较大,上树之前先做个离散化。

定义 \(d_i\) 为以 \(a_i\) 结尾的最长上升子序列的长度。

根据树状数组对最长上升子序列长度的计算方法可以得出一个结论。

对于所有满足 \(d_x\ge 2\) 的整数 \(x\) ,必然存在整数 \(y\) 满足 \(x>y\)\(d_y+1=d_x\)

也就是说,我们可以从后往前枚举 \(y\),寻找符合条件的 \(x\),若 \(x\) 存在,这个下标就是答案之一。

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <list>
using namespace std;
const int N = 2e5 + 10;
int n, a[N], b[N], m, tr[N], dp[N], req[N], res;
list<int> ret;
inline void update(int x, int v)
{
    for (; x <= m; x += (x & -x))
        tr[x] = max(tr[x], v);
}
inline int query(int x)
{
    int res = 0;
    for (; x; x -= (x & -x))
        res = max(res, tr[x]);
    return res;
}
void run()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", a + i);
        b[i] = a[i];
    }
    sort(b + 1, b + n + 1);
    m = unique(b + 1, b + n + 1) - b - 1;
    memset(tr + 1, 0, m << 2);
    memset(req + 1, 0, m << 2);
    for (int i = 1; i <= n; i++)
    {
        a[i] = lower_bound(b + 1, b + m + 1, a[i]) - b;
        // printf("%d%c", a[i], " \n"[i == n]);
    }
    res = 0;
    for (int i = 1; i <= n; i++)
    {
        dp[i] = query(a[i] - 1) + 1;
        // printf("%d%c", dp[i], " \n"[i == n]);
        res = max(res, dp[i]);
        update(a[i], dp[i]);
    }
    ret.clear();
    req[res] = 0x3f3f3f3f;
    for (int i = n; i; i--)
    {
        if (req[dp[i]] > a[i])
            ret.push_front(i), req[dp[i] - 1] = max(req[dp[i] - 1], a[i]);
    }
    printf("%d\n", ret.size());
    for (auto &i : ret)
        printf("%d ", i);
    putchar('\n');
}
int main()
{
    int T = 1;
    scanf("%d", &T);
    while (T--)
        run();
}
posted @ 2024-05-18 22:54  丝羽绫华  阅读(81)  评论(0编辑  收藏  举报