Contestant~. Rank 96. Rating +99. (+249 -150).

终于ak一场div4了

A. To My Critics

题意

给定三个数,输出最大两个数的和是否 \(\geq 10\)

思路

如题。

方便写的话可以考虑存到数组里。

时间复杂度:\(O(n \log n)?\)

对应AC代码

#pragma GCC optimize(2)

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define pipi pair<pii, pii>
#define ppii pair<int, pii>
#define pci pair<char, int>
#define fs first
#define sc second
#define pb emplace_back
#define rall(x) x.rbegin(),x.rend()
#define all(x) x.begin(),x.end()

const int N = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    vector<int> a(3);
    for(int i=0;i<3;i++) cin >> a[i];
    sort(all(a));
    if(a[1] + a[2] >= 10) cout << "YES\n";
    else cout << "NO\n";
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) solve();
}

无脑签

B. Ten Words of Wisdom

题意

给定 \(n\) 句话的长度以及价值,输出长度不超过 \(10\) 的最大价值对应的下标。

思路

如题。

时间复杂度:\(O(n)\)

对应AC代码

#pragma GCC optimize(2)

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define pipi pair<pii, pii>
#define ppii pair<int, pii>
#define pci pair<char, int>
#define fs first
#define sc second
#define pb emplace_back
#define rall(x) x.rbegin(),x.rend()
#define all(x) x.begin(),x.end()

const int N = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    int n;
    cin >> n;
    int idx = -1, vidx = -1;
    for(int i=1;i<=n;i++){
        int x, y;
        cin >> x >> y;
        if(x > 10) continue;
        if(idx == -1){
            idx = i;
            vidx = y;
        }else{
            if(y > vidx) idx = i, vidx = y;
        }
    }
    cout << idx << '\n';
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) solve();
}

无脑签

C. Word on the Paper

题意

对于一个 \(8 \times 8\) 的矩阵,初始状态下都是 "."。

现在,选定一列,并在该列插入一个连续的单词。

给定插入后的矩阵,输出单词。

思路

既然只会出现在某一列,我们直接一列一列枚举枚举所有位,将所有非 "." 字符拼接到答案上即可。

时间复杂度:\(O(n ^ 2)\)

对应AC代码

#pragma GCC optimize(2)

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define pipi pair<pii, pii>
#define ppii pair<int, pii>
#define pci pair<char, int>
#define fs first
#define sc second
#define pb emplace_back
#define rall(x) x.rbegin(),x.rend()
#define all(x) x.begin(),x.end()

const int N = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    vector<string> a(8);
    for(int i=0;i<8;i++) cin >> a[i];
    string ans = "";
    for(int j=0;j<8;j++){
        for(int i=0;i<8;i++){
            if(a[i][j] == '.') continue;
            ans += a[i][j];
        }
    }
    cout << ans << '\n';
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) solve();
}

无脑签

D. Balanced Round

题意

给定一个序列,定义操作如下:

  1. 删除一些元素;

  2. 任意排序序列

在任意次操作后,输出需要删除多少元素,使得最后的序列满足 相邻元素差的绝对值 \(\leq k\),其中 \(k\) 给定。

思路

既然要让差值的绝对值尽可能小,唯一办法就是升序或降序排序。

我们考虑升序排序,那么如果某两个相邻数的差值的绝对值大于 \(k\),若我们删掉左边的元素,那么右边的元素和其新的相邻数的差值是一定大于 \(k\) 的。另一边也是同理的。

因而我们不难发现,最佳方案就是 将排序后的序列切割为若干个区间,满足相邻区间的相邻端点之差的绝对值 \(> k\),区间内所有相邻数之差的绝对值 \(\leq k\)

那么,显然我们只能保留一个区间,那么答案就是 \(n - len_{max}\)

时间复杂度:\(O(n \log n)\)

对应AC代码

#pragma GCC optimize(2)

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define pipi pair<pii, pii>
#define ppii pair<int, pii>
#define pci pair<char, int>
#define fs first
#define sc second
#define pb emplace_back
#define rall(x) x.rbegin(),x.rend()
#define all(x) x.begin(),x.end()

const int N = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    int n, k;
    cin >> n >> k;
    vector<int> a(n + 2, inf);
    a[0] = 0;
    for(int i=1;i<=n;i++) cin >> a[i];
    sort(all(a));
    int len = 1, now = 1;
    for(int i=2;i<=n+1;i++){
        if(a[i] - a[i - 1] > k){
            len = max(len, now);
            now = 1;
        }else now ++;
    }
    cout << n - len << '\n';
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) solve();
}

有脑签

E. Cardboard for Pictures

题意

给定 \(n\) 个正方形照片的边长 \(s_i\)

现在,将每张照片裱上宽度为 \(w\) 的边框,将其变成大正方形。

给定最后所有大正方形的面积和,输出 \(w\)

保证有解。

思路

最简单的思路莫过于推式子 \(+\) 求根公式,但是存在 \(sqrt\) 卡精度和 \(long\ long\) 爆掉的情况。

当然,前者可以加优化,后者可以用 \(\_\_int128\) 代替,不过我们不妨换个思路。

我们直接考虑二分答案。

我们二分 \(w\),那么根据 \((s[i] + 2w) ^ 2\) 的大小 \(check\) 即可。

时间复杂度:\(O(n \log n)\)

对应AC代码

#pragma GCC optimize(2)

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define pipi pair<pii, pii>
#define ppii pair<int, pii>
#define pci pair<char, int>
#define fs first
#define sc second
#define pb emplace_back
#define rall(x) x.rbegin(),x.rend()
#define all(x) x.begin(),x.end()

const int N = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    int n, c;
    cin >> n >> c;
    vector<int> s(n + 1);
    for(int i=1;i<=n;i++) cin >> s[i];
    auto check = [&](int x) -> bool{
        int ans = 0;
        for(int i=1;i<=n;i++){
            ans += (s[i] + 2 * x) * (s[i] + 2 * x);
            if(ans >= c) return false;
        }
        return ans < c;
    };
    int l = 0, r = 1e9, mid;
    while(l < r){
        mid = (l + r) >> 1ll;
        if(check(mid)) l = mid + 1;
        else r = mid;
    }
    cout << l << '\n';
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) solve();
}

更搞笑的是,我一开始式子还推错了

F. We Were Both Children

题意

给定 \(n\) 只青蛙的跳跃距离 \(a_i\),所有青蛙从 \(0\) 开始向前跳跃。

现在,规定可以在 \([1, n]\) 之内选择一个节点放上陷阱,青蛙到达这个节点会被困住。

输出能困住最多多少青蛙。

思路

我们用类似预处理的方式,先记录能跳 \(p\) 距离的青蛙一共有多少只,然后在 \([1, n]\) 里枚举能跳到的点,记录该点有多少青蛙经过。

那么,最后答案就是 \([1, n]\) 中青蛙数量最多的节点对应的个数。

时间复杂度:\(O(\frac{n}{1} + \frac{n}{2} + \ldots +\frac{n}{n}) = O(n \log n)\)

对应AC代码

#pragma GCC optimize(2)

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define pipi pair<pii, pii>
#define ppii pair<int, pii>
#define pci pair<char, int>
#define fs first
#define sc second
#define pb emplace_back
#define rall(x) x.rbegin(),x.rend()
#define all(x) x.begin(),x.end()

const int N = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    int n;
    cin >> n;
    vector<int> a(n + 1), s(n + 1);
    map<int, int> mp;
    for(int i=1;i<=n;i++) {
        cin >> a[i];
        mp[a[i]]++;
    }
    for(auto [x, y] : mp){ //分块暴力
        for(int i=x;i<=n;i+=x) s[i] += y;
    }
    int ans = 0;
    for(int i=1;i<=n;i++) ans = max(ans, s[i]);
    cout << ans << '\n';
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) solve();
}

真就暴力呗

G. The Morning Star

题意

给定 \(n\) 个点的二维坐标,输出有多少不同的两个点组成的二元组,满足两个点所在直线的倾斜角为 \(0°, 45°, 90°, 135°\)

思路

这四个倾斜角对应的方程为 \(x = t, y = x + t, y = t, y = -x + t\)

可以发现,每个方程都只有一个特征值 \(t\),我们将其作为 \(map\) 的参数,分别统计每个方程不同参数下有多少个点。

那么,如果对于 \(y = x + t\),在某个特定的 \(t\) 下,满足该方程的点有 \(cnt\) 个,那么该情况下的二元组个数为 \(cnt(cnt - 1)\)

时间复杂度:\(O(n \log n)\)

对应AC代码

#pragma GCC optimize(2)

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define pipi pair<pii, pii>
#define ppii pair<int, pii>
#define pci pair<char, int>
#define fs first
#define sc second
#define pb emplace_back
#define rall(x) x.rbegin(),x.rend()
#define all(x) x.begin(),x.end()

const int N = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    int n;
    cin >> n;
    map<int, int> a, b, c, d;
    vector<pii> p(n + 1);
    for(int i = 1; i <= n; i ++){
        cin >> p[i].fs >> p[i].sc;
        a[p[i].fs] ++;
        b[p[i].sc] ++;
        c[p[i].fs - p[i].sc] ++;
        d[p[i].fs + p[i].sc] ++;
    }
    int ans = 0;
    for(auto [x, y] : a) ans += y * (y - 1);
    for(auto [x, y] : b) ans += y * (y - 1);
    for(auto [x, y] : c) ans += y * (y - 1);
    for(auto [x, y] : d) ans += y * (y - 1);
    cout << ans << '\n';
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) solve();
}

还是很暴力捏

H. The Third Letter

题意

给定 \(n\) 个人,以及 \(m\) 个限制条件。

\(i\) 个限制条件包含三个整数 \(a_i, b_i, c_i\),表示在数轴上,第 \(a_i\) 个人在第 \(b_i\) 个人前面 \(c_i\) 个单位长度位置。

输出限制是否存在冲突。

思路

首先,这是一道很经典的带权并查集,并且有一道约等于一模一样的题:Zjnu Stadium.

下面给出具体思路:

我们可以对每个连通块单独考虑。

对于每一个连通块,根据并查集的做法,我们知道每个点最后会连结到一个父节点。

那么,我们不妨在连结的时候,加入一个新的参数:距离 \(d\)

我们设一条有向边为 \(u \rightarrow v\),距离为 \(w\)\(u\) 在并查集中的父节点为 \(f1\)\(v\) 在并查集中的父节点为 \(f2\)

我们在连边的时候,也就是 \(unite\) 函数内,添加对距离 \(d\) 的更新。

我们将 \(u\) 的父节点更新为 \(f2\),那么 \(u\)\(f2\) 之间的距离就是 \(w\),但是在之前 \(u\) 的权重值是相对于 \(f1\) 计算的,所以我们需要减去 \(d[u]\),再加上 \(d[v]\),才能得到相对于 \(f2\) 的权重值。

因而,我们在 \(unite\) 函数内添加 \(d[f2] = d[u] + w - d[v]\).

当然,因为我们进行了路径压缩,所以在 \(find\) 函数里也得对应更新一下 \(d\)

时间复杂度:\(O(m \log n)\)

对应AC代码

#pragma GCC optimize(2)

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define pipi pair<pii, pii>
#define ppii pair<int, pii>
#define pci pair<char, int>
#define fs first
#define sc second
#define pb emplace_back
#define rall(x) x.rbegin(),x.rend()
#define all(x) x.begin(),x.end()

const int N = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;

struct DSU {
    vector<int> pa;
    void init(int n) { pa = vector<int>(n + 1), iota(all(pa), 0); }
    int find(vector<int> &d, int x) {
        if(pa[x] != x){
            int root = find(d, pa[x]);
            d[x] += d[pa[x]];
            return pa[x] = root;
        }
        return x;
    }
}dsu;

void solve(){
    int n, m;
    cin >> n >> m;
    vector<int> d(n + 1);
    dsu.init(n);
    bool f = true;
    while(m --){
        int u, v, w;
        cin >> u >> v >> w;
        int f1 = dsu.find(d, u), f2 = dsu.find(d, v);
        if(f1 == f2){
            if(d[v] - d[u] != w){
                f = false;
            }
        }else{
            dsu.pa[f2] = f1;
            d[f2] = d[u] + w - d[v];
        }
    }
    cout << (f ? "YES\n" : "NO\n");
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) solve();
}

一开始忘了更新 \(find\) 了,能过样例还是很抽象的