Featured image of post AtCoder Beginner Contest 456

AtCoder Beginner Contest 456

ABC456 D

出处:ABC456 D

题目大意:给定一个只包含字母 abc 的字符串 $S$。需要计算 $S$ 的非空子序列中,相邻字符均不相同的子序列个数,并将答案对 $998244353$ 取模。如果两个子序列取自不同的位置,即使它们作为字符串相同,也视为不同的子序列。

数据范围:$1 \le |S| \le 3 \times 10^5$,$S \in \{\mathtt{a},\mathtt{b},\mathtt{c}\}^*$。

思路:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
const int MOD = 998244353;
void solve() {
    string s;
    cin >> s;
    int n = sz(s);
    vl dp(3);
    ll ans = 0;
    rep(i, 0, n - 1) {
        int tem = s[i] - 'a';
        ll tot = ((dp[0] + dp[1]) % MOD + dp[2] % MOD) % MOD;
        ll tem2 = (1 + tot - dp[tem] + MOD) % MOD;
        dp[tem] = (dp[tem] + tem2) % MOD;
        ans = (ans + tem2) % MOD;
    }
    cout << ans << endl;
    return;
}

ABC456 E

出处:ABC456 E

题目大意:AtCoder 王国有 $N$ 个城市,分别编号为 $1, 2, \dots, N$。有 $M$ 条双向道路连接着若干城市,第 $i$ 条道路连接城市 $U_i$ 和 $V_i$。任意两个城市之间都可以通过若干条道路互达。在 AtCoder 王国,一周共有 $W$ 天,分别为第 $1, 2, \dots, W$ 天,第 $W$ 天的下一天又是第 $1$ 天。每个城市在一周的不同天会有不同的休息日。城市 $i$ 是否在一周的第 $j$ 天为休息日,用长度为 $W$ 的字符串 $S_i$ 描述:若 $S_i$ 的第 $j$ 个字符为 o,则第 $j$ 天是休息日;若为 x,则该天为工作日。高桥会选择一个喜欢的城市,并于第 $1$ 天中午到达。

数据范围:$1 \leq T \leq 10^5$,$1 \leq N \leq 10^5$,$N-1 \leq M \leq 10^5$,$1 \leq U_i < V_i \leq N$,$2 \leq W \leq 10$,$|S_i| = W$,$\sum N \le 10^5$,$\sum M \le 10^5$。

思路:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
void solve() {
    int n, m, w, x, y;
    cin >> n >> m;
    vvi ma(n);
    rep(i, 0, m - 1) {
        cin >> x >> y;
        ma[x - 1].push_back(y - 1);
        ma[y - 1].push_back(x - 1);
    }
    cin >> w;
    vector<string> a(n);
    rep(i, 0, n - 1) cin >> a[i];
    vvi ma2(n * w);
    vi deg(n * w);
    int tem = 0;
    rep(i, 0, w - 1) {
        rep(j, 0, n - 1) {
            if (a[j][i] == 'o') tem++;
        }
    }
    rep(i, 0, n - 1) {
        for (int& p : ma[i]) {
            rep(j, 0, w - 1) {
                if (a[i][j] == 'o' && a[p][(j + 1) % w] == 'o') {
                    ma2[j * n + i].push_back((j + 1) % w * n + p);
                    deg[(j + 1) % w * n + p]++;
                }
            }
        }
    }
    rep(i, 0, w - 1) {
        rep(j, 0, n - 1) {
            if (a[j][i] == 'o' && a[j][(i + 1) % w] == 'o') {
                ma2[i * n + j].push_back((i + 1) % w * n + j);
                deg[(i + 1) % w * n + j]++;
            }
        }
    }
    queue<int> q;
    rep(i, 0, w - 1) {
        rep(j, 0, n - 1) {
            if (a[j][i] == 'o' && deg[i * n + j] == 0) q.push(i * n + j);
        }
    }
    int tem2 = 0;
    while (!q.empty()) {
        auto node = q.front();
        q.pop();
        tem2++;
        for (int& p : ma2[node]) {
            if (--deg[p] == 0) q.push(p);
        }
    }
    if (tem2 < tem)
        cout << "Yes" << endl;
    else
        cout << "No" << endl;
    return;
}

ABC456 F

出处:ABC456 F

题目大意:Takahashi 正在为接下来的 $N$ 天制定日程安排。起初,没有任何一天是假期。他可以无限次重复以下任一操作: - 选择一个整数 $i$,其中 $1 \leq i \leq N$,并将第 $i$ 天设为假期。该操作的花费为 $A_i$。- 选择一个整数 $i$,其中 $2 \leq i \leq N-1$,且第 $i-1$ 天和第 $i+1$ 天都已经是假期,将第 $i$ 天设为假期。该操作免费。需要计算,要创建一段长度至少为 $K$ 的连续假期区块,所需的最小总花费。

数据范围:$1 \leq T \leq 2 \times 10^5$,$1 \leq K \leq N \leq 2 \times 10^5$,$1 \leq A_i \leq 10^9$,$\sum N \le 2 \times 10^5$。

思路:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
struct Info {
    ll dp[2][2];
    Info(ll x = 0) {
        dp[0][0] = 0;
        dp[0][1] = x;
        dp[1][0] = 0;
        dp[1][1] = -LLONG_MAX / 3;
    }
};
Info operator+(const Info& a, const Info& b) {
    Info c;
    rep(i, 0, 1) {
        rep(j, 0, 1) { c.dp[i][j] = -LLONG_MAX / 3; }
    }
    rep(i, 0, 1) {
        rep(j, 0, 1) {
            rep(k, 0, 1) { c.dp[i][j] = max(c.dp[i][j], a.dp[i][k] + b.dp[k][j]); }
        }
    }
    return c;
}
template <typename T>
class SegmentTree {
    int n;
    vector<T> tree;
    T merge_val(T a, T b) const { return a + b; }  // 合并子树

    void maintain(int node) {  // 维护整棵树
        tree[node] = merge_val(tree[node * 2], tree[node * 2 + 1]);
    }

    void build(const vector<T>& a, int node, int l, int r) {
        if (l == r) {
            tree[node] = a[l];
            return;
        }
        int m = (l + r) / 2;
        build(a, node * 2, l, m);
        build(a, node * 2 + 1, m + 1, r);
        maintain(node);
    }  // 建树

    void update(int node, int l, int r, int i, T val) {
        if (l == r) {
            tree[node] = val;
            return;
        }
        int m = (l + r) / 2;
        if (i <= m)
            update(node * 2, l, m, i, val);
        else
            update(node * 2 + 1, m + 1, r, i, val);
        maintain(node);
    }  // 更新i处的值为val

    T query(int node, int l, int r, int ql, int qr) const {
        if (ql <= l && r <= qr) return tree[node];
        int m = (l + r) / 2;
        if (qr <= m) return query(node * 2, l, m, ql, qr);
        if (ql > m) return query(node * 2 + 1, m + 1, r, ql, qr);
        T l_res = query(node * 2, l, m, ql, qr);
        T r_res = query(node * 2 + 1, m + 1, r, ql, qr);
        return merge_val(l_res, r_res);
    }  // 查询[ql,qr]的值

    int find_first(int node, int l, int r, int ql, int qr, T val) const {
        if (r < ql || l > qr) return -1;
        if (tree[node] < val) return -1;
        if (l == r) return l;
        int m = (l + r) >> 1;
        int res = find_first(node << 1, l, m, ql, qr, val);
        if (res != -1) return res;
        return find_first(node << 1 | 1, m + 1, r, ql, qr, val);
    }  // 若遇到固定左端点的情况,需要使用全局变量(或者传入引用)记录前缀分段最大值,加一个被待求区间完全覆盖的剪枝

    int find_last(int node, int l, int r, int ql, int qr, T val) const {
        if (r < ql || l > qr) return -1;
        if (tree[node] < val) return -1;
        if (l == r) return l;
        int m = (l + r) >> 1;
        int res = find_last(node << 1 | 1, m + 1, r, ql, qr, val);
        if (res != -1) return res;
        return find_last(node << 1, l, m, ql, qr, val);
    }

public:
    SegmentTree(int n, T init_val) : SegmentTree(vector<T>(n, init_val)) {}

    SegmentTree(const vector<T>& a) : n(a.size()), tree(2 << bit_width(a.size() - 1)) { build(a, 1, 0, n - 1); }  // 传入一个数组维护

    void update(int i, T val) { update(1, 0, n - 1, i, val); }  // 更新i的值为val

    T query(int ql, int qr) const { return query(1, 0, n - 1, ql, qr); }  // 查询[ql,qr]的值

    T get(int i) const { return query(1, 0, n - 1, i, i); }  // 取出i处的值

    int find_first(int ql, int qr, T val) const { return find_first(1, 0, n - 1, ql, qr, val); }  // 查询[ql,qr]中第一个满足条件的下标

    int find_last(int ql, int qr, T val) const { return find_last(1, 0, n - 1, ql, qr, val); }  // 查询[ql,qr]中最后一个满足条件的下标
};
void solve() {
    int n, k;
    cin >> n >> k;
    vl a(n);
    rep(i, 0, n - 1) cin >> a[i];
    vector<Info> b(n);
    rep(i, 0, n - 1) b[i] = Info(a[i]);
    SegmentTree<Info> tree(b);
    ll ans = LLONG_MAX;
    vl pre(n + 1);
    rep(i, 1, n) pre[i] = pre[i - 1] + a[i - 1];
    rep(i, 0, n - k) {
        ll tem = pre[i + k] - pre[i];
        ll tem2 = 0;
        if (k > 2) tem2 = max(tree.query(i + 1, i + k - 2).dp[0][0], tree.query(i + 1, i + k - 2).dp[0][1]);
        ans = min(ans, tem - tem2);
    }
    rep(i, 0, n - k - 1) {
        ll tem = pre[i + k + 1] - pre[i];
        ll tem2 = 0;
        if (k + 1 > 2) tem2 = max(tree.query(i + 1, i + k - 1).dp[0][0], tree.query(i + 1, i + k - 1).dp[0][1]);
        ans = min(ans, tem - tem2);
    }
    cout << ans << endl;
    return;
}