退役老登来凑个热闹,标题好中二(

A. 一起奏响历史之音!

题意

给定 个数,判断是否由 组成。

思路

如题,模拟即可。

时间复杂度:

对应AC代码

void solve() {
    int n = 7;
    bool ok = true;
    while(n --) {
        int x;
        cin >> x;
        if(x == 4 || x == 7) ok = false;
    }
    cout << (ok ? "YES" : "NO") << '\n';
}

主打一个手速

B. 能去你家蹭口饭吃吗

题意

给定长为 的序列 。记 小于序列 中至少 个数字,求 的最大值。

思路

只需小于序列 中的第 大即可。

记序列 中的第 大的下标为 ,则答案为

时间复杂度:

对应AC代码

void solve() {
    int n;
    cin >> n;
    vector<int> a(n + 1);
    for(int i=1;i<=n;i++) cin >> a[i];
    sort(a.begin(), a.end());
    cout << a[n / 2 + 1] - 1 << '\n';
}

简单排个序

C. 字符串外串

题意

对于一个字符串,定义其可爱度为满足 至少存在一个长为 的连续子串 和一个长为 的非连续子序列(非空)、且 的最大值。

给定两个整数 ,构造一个长为 且可爱度为 的小写字母字符串。

思路

首先我们来考虑一下如何求一个字符串的可爱度,这同时也是 题要完成的任务。

举例来说,有这样一个字符串 ,钦定其为 。第一眼想到的可能是这种取法: 。此时我们有一个有趣的发现: 中的 完全可以替换为 中的 ,同时 中的 也可以替换为 中的 ,做完这些后, 只有最后一个字符不在同一位置。

更复杂的字符串也是一样的,因为 都是按照同样的顺序从字符串取出的。

所以我们有一个很明显的贪心思路:假设 的最后一个字符为 ,那么从右往左取第一个 作为 的最后一个字符,第二个 作为 的最后一个字符,并将第二个 左侧的所有字符拼接在 前面。

同时我们需要翻转一下字符串,求出反向的最大值,最后两者取最大值即可求出可爱度。

现在我们回过头来看看 题。

让我们先从左往右看。首先,作为最后一个不同的字符 ,其出现次数至少为 ,且可爱度应该取倒数第二个 出现的位置。

那么无论以哪种字符结尾,其倒数第二个字符的下标需满足 。换句话说,我们希望构造出的字符串应该类似于下面的样子:

,最后两个 之间既不包含 ,也没有重复出现的字符。

最简单的构造自然是 ,可惜从右往左看时,可爱度过大了。所以我们需要一个对称的或者有周期性的构造。

不妨考虑周期性的构造。如果要满足上面的条件,那么周期至少为 ,否则两个 间会出现重复的字符。

那么我们只需在 中取出 个字符,然后一直重复即可。

如上,当 或者 时,判定无解,否则不难证明构造的正确性。

时间复杂度:

对应AC代码

void solve() {
    int n, m;
    cin >> n >> m;
    if(n < m + 1 || n > m + 26) {
        cout << "NO\n";
        return;
    }
    cout << "YES\n";
    for(int i=0;i<n;i++) {
        cout << (char)('a' + i % (n - m));
    }
    cout << '\n';
}

没对上脑电波

D. 字符串里串

题意

题的定义下,给定字符串,求可爱度。

思路

详见 题题解。

需要注意的是,这种做法可能会出现 包含空串的情况,比如说 。此时需要特判一下。

时间复杂度:

对应AC代码

void solve() {
    int n;
    cin >> n;
    string s;
    cin >> s;
    s = " " + s;
    vector<vector<int>> idx(26);
    for(int i=1;i<=n;i++) {
        idx[s[i] - 'a'].emplace_back(i);
    }
    int ans = 0;
    for(auto &vec : idx) {
        if(vec.size() <= 1) continue;
        if(vec[1] != n) ans = max(ans, n - vec[1] + 1);
        if(vec[vec.size() - 2] != 1) ans = max(ans, vec[vec.size() - 2]);
    }
    cout << ans << '\n';
}

观察可得

E. 一起走很长的路!

题意

给定一个长为 的序列 ,对于一个区间 ,若对于所有 ,均有 ,则该区间为完美的。

给定 次独立询问,每次询问给定一个区间 ,定义一次操作为选定一个元素并将其 ,确定使得区间完美需要的最小操作数。

思路

首先,我们只需增大 即可,因为增大 会影响到后面所有的条件。

那么我们只需求出 ,并对 即可。

考虑前缀和,我们只需求出 ,然后对

因为不存在修改,所以简单的 ST 表即可解决。

时间复杂度:

对应AC代码

template<class Info>
struct SparseTable {
    int n;
    vector<vector<Info>> info;
    vector<int> lg;
    SparseTable() : n(0) {}
    void initLg() {
        lg = vector<int>(n + 1, -1);
        for(int i=1;i<=n;i++) lg[i] = lg[i / 2] + 1;
    }
    explicit SparseTable(int _n, vector<Info> _info) : n(_n) {
        initLg();
        info = vector<vector<Info>>(n + 1, vector<Info>(lg[n] + 1));
        for(int i=1;i<=n;i++) info[i][0] = _info[i];
        for(int i=1;i<=lg[n];i++){
            for(int j=1;j+(1ll<<(i-1))<=n;j++){
                info[j][i] = info[j][i - 1] + info[j + (1ll << (i - 1))][i - 1];
            }
        }
    }
    Info rangeQuery(int l, int r) {
        int len = lg[r - l + 1];
        return info[l][len] + info[r - (1ll << len) + 1][len];
    }
};

struct Info{
    int val = 0;
};

Info operator+(const Info &a, const Info &b) {
    Info c;
    c.val = max(a.val, b.val); //RMQ
    return c;
}

void solve() {
    int n, q;
    cin >> n >> q;
    vector<int> a(n + 1);
    for(int i=1;i<=n;i++) cin >> a[i];
    vector<int> pre(n + 1);
    vector<Info> info(n + 1);
    pre[1] = a[1];
    info[1].val = -inf;
    for(int i=2;i<=n;i++) {
        info[i].val = a[i] - pre[i - 1];
        pre[i] = pre[i - 1] + a[i];
    }
    SparseTable<Info> st(n + 1, info);
    while(q --) {
        int l, r;
        cin >> l >> r;
        if(l == r) {
            cout << 0 << '\n';
            continue;
        }
        cout << max(0, st.rangeQuery(l + 1, r).val + pre[l - 1]) << '\n';
    }
}

如果反着减可能就懵逼了(

F. 一起找神秘的数!

题意

给定区间 ,求满足 以及 的二元组 数量。

思路

首先,显然可以发现当 时等式成立,不妨大胆猜想,大胆乱交,小心求证。

我们不妨按位考虑,先无视异或,可以得到下面的真值表:

可以发现, ,换句话说, 。从而得到 ,证明了我们的猜想。

那么答案就是区间内数字的个数,也就是

时间复杂度:

对应AC代码

void solve() {
    int l, r;
    cin >> l >> r;
    cout << r - l + 1 << '\n';
}

乱猜就完事了!(

G. 一起铸最好的剑!

题意

给定两个整数 ,求出使得 最小的 的值。

思路

暴力,打到第一次超过 时即可,特判

时间复杂度:

对应AC代码

void solve() {
    int n, m;
    cin >> n >> m;
    long long x = m;
    pair<long long, int> ans = {abs(x - n), 1};
    if(m != 1) {
        for(int i=2;x<n;i++) {
            x *= m;
            ans = min(ans, {abs(x - n), i});
        }
    }
    cout << ans.second << '\n';
}

人均挂一发(

H. 一起画很大的圆!

题意

给定四个整数 ,分别代表直线 ,且满足

在直线所围得的矩形的边上取三个不同的整点,使得外接圆面积最大,并给出方案。

思路

不妨从曲率的角度考虑,如果三点共线的话,那么面积将为无穷大,所以我们只需让三点尽可能贴近一条直线。

同时,由正弦定理可知,外接圆半径和边长也有关系,所以我们考虑让斜边尽可能长。

于是我们只需用类似于样例的方法,先取长边的一端作为点 ,然后在长边上取离 最近的点 ,最后取在短边且距离长边另一端最近的点 ,即可得到最大面积。

时间复杂度:

对应AC代码

void solve() {
    int a, b, c, d;
    cin >> a >> b >> c >> d;
    if (b - a <= d - c) {
        cout << a << ' ' << c << '\n';
        cout << a << ' ' << c + 1 << '\n';
        cout << a + 1 << ' ' << d << '\n';
    } else {
        cout << a << ' ' << d << '\n';
        cout << a + 1 << ' ' << d << '\n';
        cout << b << ' ' << d - 1 << '\n';
    }
}

差点取对角线去了,显然不如这种方案来的共线

I. 一起看很美的日落!

待补充

J. 数据时间?

待补充

K. 可以分开吗?

题意

给定一个 矩阵,若两个格子有至少一条公共边,则视为在同一连通块中。当连通块中全为 时,该连通块将掉落。

定义一次操作为将矩阵中的 抹除,输出使得至少一块连通块掉落需要的最小操作数。

思路

不妨考虑使用并查集维护所有全 连通块,从而快速确定每个为 的格子属于哪个连通块。

然后我们只需求出每个连通块需要多少次操作使其能掉落,然后取最小值即可。

不妨枚举所有为 的格子,并判断与其相邻的最多 个为 的格子各属于哪个连通块,并得到一个连通块的不可重集合,然后给集合中的每个连通块的答案累加 即可。

时间复杂度:

对应AC代码

struct DSU {
    vector<int> pa, siz;
    void init(int n) { pa = vector<int>(n + 1), siz = vector<int>(n + 1, 1), iota(all(pa), 0); }
    int find(int x) { while(x != pa[x]) x = pa[x] = pa[pa[x]]; return x; }
    bool unite(int u, int v) {
        int f1 = find(u), f2 = find(v);
        if (f1 == f2) return false;
        siz[f1] += siz[f2];
        pa[f2] = f1;
        return true;
    }
    int size(int x){ return siz[find(x)]; }
}dsu;

void solve() {
    int n, m;
    cin >> n >> m;
    vector<vector<int>> a(n + 1, vector<int>(m + 1));
    for(int i=1;i<=n;i++) {
        string s;
        cin >> s;
        for(int j=1;j<=m;j++) a[i][j] = s[j - 1] - '0';
    }
    auto p = [&](int x, int y) { return (x - 1) * m + y; };
    dsu.init(n * m);
    for(int x=1;x<=n;x++) {
        for(int y=1;y<=m;y++) {
            if(a[x][y] == 0) continue;
            for(auto [dx, dy] : {pii{-1, 0}, {1, 0}, {0, -1}, {0, 1}}) {
                int tx = x + dx, ty = y + dy;
                if(tx >= 1 && tx <= n && ty >= 1 && ty <= m && a[tx][ty] == 1) {
                    dsu.unite(p(x, y), p(tx, ty));
                }
            }
        }
    }
    vector<int> cnt(n * m + 1);
    for(int x=1;x<=n;x++) {
        for(int y=1;y<=m;y++) {
            if(a[x][y] == 1) continue;
            set<int> adj;
            for(auto [dx, dy] : {pii{-1, 0}, {1, 0}, {0, -1}, {0, 1}}) {
                int tx = x + dx, ty = y + dy;
                if(tx >= 1 && tx <= n && ty >= 1 && ty <= m && a[tx][ty] == 1) {
                    adj.emplace(dsu.find(p(tx, ty)));
                }
            }
            for(auto &it : adj) cnt[it] ++;
        }
    }
    int ans = inf;
    for(int i=1;i<=n*m;i++) {
        if(cnt[i] == 0) continue;
        ans = min(ans, cnt[i]);
    }
    if(ans == inf) cout << 0 << '\n';  // 全是蓝的
    else cout << ans << '\n';
}

写搜索好像有点麻烦(

L. 还会再见吗?

待补充

M. 那是我们的影子

待补充