AtCoder ABC 365题解

前言

本文集结网络上的题解,方便查阅。

A - Leap Year

题目大意

判断闰年

解题思路

根据题目模拟即可。

code

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n;
    scanf("%d", &n);
    if(n % 4 != 0) {
        printf("365\n");
    } else if(n % 4 == 0 && n % 100 != 0) {
        printf("366\n");
    } else if(n % 100 == 0 && n % 400 != 0) {
        printf("365\n");
    } else if(n % 400 == 0) {
        printf("366\n");
    }
    return 0;
}

B - Second Best

题目大意

输出第二大的数的下标。

解题思路

对下标排序,输出第二个即可。

code

#include <bits/stdc++.h>
using namespace std;
int a[105];
int ind[105];
bool cmp(int i, int j) {
    return a[i] > a[j];
}
int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", a + i);
        ind[i] = i;
    }
    sort(ind + 1, ind + 1 + n, cmp);
    printf("%d\n", ind[2]);
    return 0;
}

C - Transportation Expenses

题目大意

给定n个人的交通费,求出最大的交通补贴额 \(x\) ,使得总交通补贴额 \(\sum \min(x,a_i)\) 不超过 \(m\)

解题思路

显然,如果 \(\sum a_i \leq m\) ,则无论 \(x\) 多大,总交通补贴额永远不会超过 \(m\) 。此时应当输出 infinite

否则,二分查找 \(x\) ,每一次计算补贴额度就直接 \(for\) 循环。求出 \(\sum \max(x,a_i)\) 即可。

code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[200005];
ll check(int x, int n) {
    ll sum = 0;
    for (int i = 1; i <= n; i++) {
        sum += min(a[i], x);
    }
    return sum;
}
int search(int l, int r, int n, ll m) {
    int mid;
    while (l < r) {
        mid = (l + r + 1) / 2;
        if (check(mid, n) > m) r = mid - 1;
        else l = mid;
    }
    return l;
}
int main() {
    int n, maxx;
    ll m, cnt = 0;
    scanf("%d%lld", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d", a + i);
        cnt += a[i];
        maxx = max(maxx, a[i]);
    }
    if (cnt <= m) {
        printf("infinite\n");
        return 0;
    }
    printf("%d\n", search(1, maxx, n, m));
    return 0;
}

D - AtCoder Janken 3

题目大意

\(Takahashi\)\(Aoki\) 玩石头剪刀布,给定 \(Aoki\) 出的手势,当

  • \(Takahashi\) 从来没有输过。
  • \(Takahashi\) 没有出过两个连续的手势。

时,\(Takahashi\) 最多能赢多少局?

解题思路

考虑 \(Takahashi\) 能出的手势只与 \(Aoki\) 这一局出的手势和 \(Takahashi\) 上一局出的手势有关,所以设 \(dp[i][j]\) 表示前 \(i\) 局,\(Takahashi\) 在第 \(i\) 局出了 \(j\) 的最大获胜数,每一个 \(dp[i][j]\)\(dp[i - 1][*]\) 转移过来即可。

同时,需要注意,如果 \(j\) 手势会使 \(Takahashi\) 输掉第 \(i\) 局比赛,则 \(dp[i][j]\) 等于极小值,

code

#include <bits/stdc++.h>
using namespace std;
int dp[3], dp2[3];
int main() {
    int n;
    string s;
    cin >> n >> s;
    map<char, int> tie{{'R', 0}, {'P', 1}, {'S', 2}};
    map<int, int> win{{0, 1}, {1, 2}, {2, 0}};
    for (int i = 0; i < n; i++) {
        dp2[0] = -0x3f3f3f3f;
        dp2[1] = -0x3f3f3f3f;
        dp2[2] = -0x3f3f3f3f;
        int pid = tie[s[i]];
        int id = win[pid];
        for (int j = 0; j < 3; j++) {
            if (j != id) {
                dp2[id] = max(dp2[id], dp[j] + 1);
            }
            if (j != pid) {
                dp2[pid] = max(dp2[pid], dp[j]);
            }
        }
        dp[0] = dp2[0];
        dp[1] = dp2[1];
        dp[2] = dp2[2];
    }
    printf("%d\n", max(dp[0], max(dp[1], dp[2])));
    return 0;
}

E - Xor Sigma Problem

题目大意

给定 \(A_1,\ A_2,\ ...,\ A_N\) ,求

\[\sum\limits^{n - 1}_{i = 1}\sum\limits^{n}_{j = i + 1}(A_i \oplus A_i + 1 \oplus ... \oplus A_j). \]

解题思路

考虑每一个二进制位对结果的贡献。

\[\sum\limits^{n - 1}_{i = 1}\sum\limits^{n}_{j = i + 1}(A_i \oplus A_i + 1 \oplus ... \oplus A_j) = \sum\limits^{31}_{i = 0} 2^icnt_i \]

考虑如何求 \(cnt\)

对于二进制第 \(k\) 位,用 \(odd_i\) 表示 \(A_i\) 的二进制位的第 $k $ 位是不是 \(1\) 。然后考虑有多少个区间异或和为 \(1\)

区间异或和和区间和一样,也可以使用前缀和优化。令 \(sum_i = A_1 \oplus A_2 \oplus ... \oplus A_i\) ,则 \(A_i \oplus A_{i + 1} \oplus ... \oplus A_j = sum_j \oplus sum_{i - 1}\)

因为我们要求有多少对 \((i,j)\) 满足 \(sum_j \oplus sum_{i - 1} = 1\) ,所以我们可以枚举 \(j\) ,再枚举 对应的 \(i\) 的数量。

因为:

  • 如果 \(sum_j = 1\) ,则 \(sum_{i - 1} = 0\)
  • 如果 \(sum_j = 0\) ,则 \(sum_{i - 1} = 1\)

所以,我们在枚举 \(j\) 时,实时维护 \(sum_i = 1\)\(sum_i = 0\) 的数量然后根据 \(odd_j\) 的值,就可以知道 \(i\) 的值。

得到所有符合条件的 \((i,j)\) 的数量即 \(cnt_k\) 后,计算出每一位所做的贡献 \(2^icnt\) ,每一位所作贡献之和即为答案。

注意,上面的方案包含 \(i = j\) 情况,然而题目要求 \(i < j\) ,所以最后还需要减去一个 \(\sum_{i = 1}^n A_i\)

code

#include <bits/stdc++.h>
#define MAX_N 200005
using namespace std;
using LL = long long;
int a[MAX_N];
LL solve(int k, int n) {
    vector<int> odd(n + 1, 0);
    for (int i = 1; i <= n; i++) {
        odd[i] = (a[i] >> k) & 1;
    }
    for (int i = 2; i <= n; i++) {
        odd[i] ^= odd[i - 1];
    }
    LL ans = 0;
    int m[2] = {1, 0};
    for (int i = 1; i <= n; i++) {
        ans += m[odd[i] ^ 1];
        m[odd[i]]++;
    }
    return ans;
}
int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", a + i);
    }
    LL ans = 0;
    for (int i = 0; i <= 31; i++) {
        ans += (1LL << i) * solve(i, n);
    }
    for (int i = 1; i <= n; i++) {
        ans -= a[i];
    }
    printf("%lld\n", ans);
    return 0;
}

F - Takahashi on Grid

题目大意

\(Takahashi\) 站在一个网格上,求从 \((sx,sy)\) 走到 \((tx,ty)\) 的最短距离。

注意,\((i,j)\) 表示第 \(i\) 列,第 \(j\) 行。

解题思路

实际上,\(Takahashi\) 的行走方案分为上下移动和左右移动,我们可以重复执行这两步直到走到第 \(tx\) 列。

  • 第一步:让 \(Takahashi\) 先一直向右移动,直到碰到墙,也就是移动到 \(y < l[x + 1]\)\(y > r[x + 1]\)
  • 第二步:再上下移动到 \(l[x + 1] \leq y \leq r[x + 1]\)

为了快速的找到每一次 \(Takahashi\) 碰到哪一列的墙,我们维护两个ST表 \(L\)\(R\) ,每次二分查找一个 \(nxt\) ,使得 \(nxt\) 是第一个使得 \(L.query(x,nxt) > y\)\(R.query(x,nxt) < y\) 的数。这个方法的时间复杂度仅有 \(\textrm{O}(\log n)\)

为了能够快速知道执行完第一部和第二步后 \(Takahashi\) 在哪里,我们预处理一个倍增数组 \(run[i][x][0/1]\) ,即 \(Takahashi\) 从第 \(x\) 列的上/下端点开始走了 \(2^i\) 次第一步和第二步后,会走到哪一列的上/下端点,用了多少步。

对于每一次询问,我们先执行一次第一步和第二步使得 \(Takahashi\) 站在某一列的上/下端点,统计这些操作用了多少步。之后,通过倍增数组 \(run\) 来计算 \(Takahashi\) 走到 \((tx,ty)\) 需要用多少步。注意,这里用倍增数组可能不能直接到达 \((tx,ty)\) ,所以需要在执行一次第一步,最后上下移动到 \((tx,ty)\)

code

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
using VT = vector<int>;
using VVT = vector<VT>;
using A = array<LL, 3>;
using VA = vector<A>;
using VVA = vector<VA>;
using VVVA = vector<VVA>;
class SparseTable {
private:
    using F = function<int(const int &, const int &)>;
    int n;
    VVT m;
    F fun;

public:
    void init(const VT &a, F f) {
        fun = f;
        n = a.size();
        int maxLog = 32 - __builtin_clz(n);
        m.resize(n + 1, VT(maxLog + 1));
        for (int i = 1; i < n; i++) {
            m[i][0] = a[i];
        }
        for (int j = 1; j <= maxLog; j++) {
            for (int i = 1; i <= n - (1 << (j - 1)); i++) {
                m[i][j] = fun(m[i][j - 1],
                              m[i + (1 << (j - 1))][j - 1]);
            }
        }
    }
    int query(int from, int to) {
        int lg = 32 - __builtin_clz(to - from + 1) - 1;
        return fun(m[from][lg], m[to - (1 << lg) + 1][lg]);
    }
};
VVVA run;
SparseTable L;
SparseTable R;
VT l;
VT r;
int n;
void jiebao(const A &arr, LL &a, LL &b, LL &c) {
    a = arr[0];
    b = arr[1];
    c = arr[2];
}
int step(int x, int y) {
    int l = x, r = n + 1;
    while (l < r) {
        int mid = (l + r) / 2;
        if (L.query(l, mid) <= y && R.query(l, mid) >= y) {
            l = mid + 1;
        } else {
            r = mid;
        }
    }
    return l;
}
LL solve(int sx, int sy, int tx, int ty) {
    if (sx == tx) {
        return abs(sy - ty);
    }
    if (sx > tx) {
        swap(sx, tx);
        swap(sy, ty);
    }
    int nxt = step(sx, sy);
    if (nxt > tx) {
        return tx - sx + abs(sy - ty);
    }
    int d = (sy < l[nxt] ? 0 : 1);
    LL nnxt, dd, ccost;
    LL ret = nxt - sx + (d ? sy - r[nxt] : l[nxt] - sy);
    sx = nxt;
    for (int i = 31; i >= 0; i--) {
        jiebao(run[i][sx][d], nnxt, dd, ccost);
        if (nnxt <= tx) {
            ret += ccost;
            sx = nnxt;
            d = dd;
        }
    }
    ret +=
        tx - sx + (d ? abs(ty - r[sx]) : abs(l[sx] - ty));
    return ret;
}
int main() {
    scanf("%d", &n);
    l.resize(n + 5);
    r.resize(n + 5);
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &l[i], &r[i]);
    }
    l[n + 1] = 0x3f3f3f3f;
    r[n + 1] = -0x3f3f3f3f;
    run.resize(40, VVA(n + 1, VA(2, A())));
    L.init(l, [](int i, int j) { return max(i, j); });
    R.init(r, [](int i, int j) { return min(i, j); });
    LL nxt, d, cost, nnxt, dd, ccost;
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < 2; j++) {
            int x = i, y = (j ? r[i] : l[i]);
            nxt = step(x, y);
            if (nxt == n + 1) {
                run[0][i][j] = {n + 1, 0, n - i + 1};
            } else if (y < l[nxt]) {
                run[0][i][j] = {
                    nxt, 0, nxt - i + l[nxt] - y};
            } else {
                run[0][i][j] = {
                    nxt, 1, nxt - i + y - r[nxt]};
            }
        }
    }
    for (int i = 1; i <= 32; i++) {
        for (int j = 1; j <= n; j++) {
            for (int k = 0; k < 2; k++) {
                jiebao(run[i - 1][j][k], nxt, d, cost);
                if (nxt == n + 1) {
                    run[i][j][k] = {n + 1, 0, cost};
                } else {
                    jiebao(run[i - 1][nxt][d],
                           nnxt,
                           dd,
                           ccost);
                    run[i][j][k] = {nnxt, dd, cost + ccost};
                }
            }
        }
    }
    int q, sx, sy, tx, ty;
    scanf("%d", &q);
    while (q--) {
        scanf("%d%d%d%d", &sx, &sy, &tx, &ty);
        printf("%lld\n", solve(sx, sy, tx, ty));
    }
    return 0;
}

G - AtCoder Office

题目大意

\(n\) 个人在办公室外面,给你 \(m\) 条退出勤记录,求 \(L\)\(U\) 共同处在办公室的总时间。

解题思路

根号分治。
定义一个阈值 \(\theta \approx \sqrt{m}\) ,所有记录条数不小于 \(\theta\) 的人为 \(a\) 类人,记录条数小于 \(\theta\) 的人为 \(b\) 类人。

首先预处理出所有 \(a\) 类人和其他人的答案。如果询问的两个人中有一个人是 \(a\) 类人,则直接输出答案,否则暴力求出答案。

为了求出每一个 \(a\) 类人和其他人对应的答案,我们定义一个数组 $tmp[i] $ ,表示第 \(i\) 条记录的时间。注意,此时间非彼时间,可以理解为当这一个 \(a\) 类人进入办公室时,时间开始流逝,当这个 \(a\) 类人出办公室时,时间暂停。

img

图中上面的时间轴是正常的时间,下面的时间轴是处理过后的 \(tmp\) 数组对应的时间。这样就可以利用 \(tmp\) 数组快速求出 \(a\) 类人和其他人在一个时间区间内共处在办公室的总时间。

由于最多有 \(\cfrac{m}{\theta} \approx \sqrt{m}\)\(a\) 类人,所以预处理所有 \(a\) 类人和其他人的答案需要的时间为 \(\textrm{O}((n + m)\sqrt{m})\)。而暴力求解的时间复杂度为 \(\textrm{O}(\sqrt{m})\)。所以最终的时间复杂度为 \(\textrm{O}((n + m + q )\sqrt{m})\) ,其中 \(q\) 为询问次数。

code

#include <bits/stdc++.h>
#define MAX_N 200005
using namespace std;
int n, m, q, tot;
int T[MAX_N], P[MAX_N];
int ma[MAX_N], ans[1000][MAX_N];
vector<int> t[MAX_N];
int tmp[MAX_N];
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++) {
        scanf("%d%d", &T[i], &P[i]);
        t[P[i]].push_back(i);
    }
    for (int i = 1; i <= n; i++) {
        if (t[i].size() < 500) continue;
        ma[i] = ++tot;
        int f = 0;
        for (int j = 1; j <= m; j++) {
            if (f) tmp[j] = T[j] - T[j - 1];
            else tmp[j] = 0;
            if (P[j] == i) f ^= 1;
        }
        for (int j = 1; j <= m; j++) tmp[j] += tmp[j - 1];
        for (int j = 1; j <= n; j++) {
            if (i == j) continue;
            for (int k = 0, l = t[j].size(); k < l;
                 k += 2) {
                ans[tot][j] +=
                    tmp[t[j][k + 1]] - tmp[t[j][k]];
            }
        }
    }
    scanf("%d", &q);
    int a, b, l1, l2, lt, f1, f2, p1, p2, ans2;
    while (q--) {
        scanf("%d%d", &a, &b);
        if (ma[a]) {
            printf("%d\n", ans[ma[a]][b]);
            continue;
        }
        if (ma[b]) {
            printf("%d\n", ans[ma[b]][a]);
            continue;
        }
        p1 = p2 = ans2 = f1 = f2 = lt = 0;
        l1 = t[a].size();
        l2 = t[b].size();
        while (p1 < l1 && p2 < l2) {
            if (t[a][p1] < t[b][p2]) {
                if (f1 && f2) ans2 += T[t[a][p1]] - lt;
                f1 ^= 1;
                lt = T[t[a][p1]];
                p1++;
            } else {
                if (f1 && f2) ans2 += T[t[b][p2]] - lt;
                f2 ^= 1;
                lt = T[t[b][p2]];
                p2++;
            }
        }
        printf("%d\n", ans2);
    }
    return 0;
}
posted @ 2024-08-07 20:59  sxl701817  阅读(198)  评论(0)    收藏  举报