Martian148's blog Martian148's blog
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档

Martian148

一只热爱文科的理科生
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档
  • codeforses 题解

  • atcoder 题解

    • AtCoder Beginner Contest 344 A-G 题解
    • AtCoder Beginner Contest 345 A-F 题解
    • AtCoder Beginner Contest 346 A-G 题解
    • AtCoder Beginner Contest 347 A-G 题解
      • A - Divisible
        • Quesiton
        • Solution
        • Code
      • B - Substring
        • Question
        • Solution
        • Code
      • C - Ideal Holidays
        • Soluiton
        • Code
      • D - Popcount and XOR
        • Solution
        • Code
      • E - Set Add Query
        • Solution
        • Code
      • F - Non-overlapping Squares
        • Solution
        • Code
      • G - Grid Coloring 2
        • Solution
        • Code
    • AtCoder Beginner Contest 353 A-G 题解
    • AtCoder Beginner Contest 359 A-G 题解
    • AtCoder Beginner Contest 366 A-F 题解
    • AtCoder Beginner Contest 369 A-F 题解
  • XCPC 题解

  • 校训练赛题解

  • 牛客题解

  • 蓝桥杯题解

  • 典题选讲

  • 杂题

  • 算法题解
  • atcoder 题解
martian148
2024-08-02
目录

AtCoder Beginner Contest 347 A-G 题解

AtCoder Beginner Contest 347 (opens new window)

# A - Divisible

# Quesiton

给你正整数 NNN 和 KKK ,以及长度为 NNN 的序列 AAA。

提取 AAA 中所有是 KKK 倍数的元素,除以 KKK ,并打印商。

# Solution

判断 Ai%KA_i \% KAi​%K 的值是否为 000,如果非 000 则表示可以整除

# Code

#include <bits/stdc++.h>
using namespace std;
int main() {
    vector<int> ans;
    int n, k; cin >> n >> k;
    for (int i = 1; i <= n;i++) {
        int x; cin >> x;
        if (x % k == 0) {
            ans.push_back(x / k);
        }
    }
    for (auto x : ans)
        cout << x << " ";
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# B - Substring

# Question

给你一个由小写英文字母组成的字符串 SSS 。 SSS 有多少个不同的非空子串?

子串是连续的子序列。例如,xxx是yxxxy的子串,但不是xxyxx的子串。

# Solution

使用 substr() 函数取字串

然后用 set<string> 去重即可

# Code

#include <bits/stdc++.h>
using namespace std;
int main() {
    string s; cin >> s;
    set<string> st;
    for (int L = 1; L <= s.size(); L++) 
        for (int i = 0; i + L - 1 < s.size(); i++) {
            string t = s.substr(i, L);
            st.insert(t);
        }
    cout << st.size() << endl;
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# C - Ideal Holidays

# Soluiton

考虑把一个 DiD_iDi​ 都压缩到一个周期中

Di=(Di−1)%(A+B)D_i = (D_i-1)\%(A+B)Di​=(Di​−1)%(A+B)

即 000 表示一周的第一天,A+B−1A+B-1A+B−1 表示一周的最后一天

我们需要找一个 iii 使得 [i,i+A)[i,i+A)[i,i+A) 天包括所有的 DiD_iDi​

如果 i+A>A+Bi+A>A+Bi+A>A+B ,即 i>Bi>Bi>B,那么就可以转化成:

[0,i−B)∪[i,n)[0,i-B)\cup[i,n)[0,i−B)∪[i,n)

具体实现是,转化为判断

排序后,是否存在一个 DiD_iDi​ 和 Di+1D_{i+1}Di+1​ 的中间能插入一个 BBB

# Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
    int n; cin >> n;
    ll A, B; cin >> A >> B;
    vector<ll> D(n);
    for (int i = 0; i < n; i++) {
        cin >> D[i];
        D[i] = (D[i] - 1) % (A + B);
    }
    sort(D.begin(), D.end());
    for (int i = 1; i < n; i++)
        if (D[i] - D[i - 1] >= B) {
            cout << "Yes" << '\n';
            return 0;
        }
    if (D.back() - D.front() < A) {
        cout <<  "Yes" << '\n';
        return 0;
    }
    cout << "No" << endl;
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# D - Popcount and XOR

# Solution

由于异或的性质,每一位分开来看,如果为 111,说明两者肯定有一个为 000,一个为 111,如果为 000,有可能都为 000,或都为 111

我们设 CCC 的二进制位数 111 的个数为 cntccnt_ccntc​

所以 a+b<cntca+b<cnt_ca+b<cntc​ 或者 a+b−cntca+b-cnt_ca+b−cntc​ 是一个奇数的情况肯定是不合法的

然后模拟放 111 的过程

如果 CCC 的某一位为 111,选择 aaa 或 bbb 其中的一个放 111

在 CCC 111 的位置都放完后,如果 a,ba,ba,b 还有剩余,就在 CCC 为 000 的位置同时在 a,ba,ba,b 的相同位置放上 111

使用 bitset 可以很好的模拟

# Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
    int a, b;
    ll C; 
    cin >> a >> b >> C;
    bitset<60> bit_C(C), bit_A, bit_B;
    if (a + b < bit_C.count() || (a + b - bit_C.count()) % 2 == 1) {cout << "-1" << '\n'; return 0;}
    int cnt = (a + b - bit_C.count()) / 2;
    a -= cnt; b -= cnt;
    for (int i = 0; i < 60; i++) {
        if (bit_C[i]) {
            if (a) {bit_A[i] = 1; a--;}
            else if (b) {bit_B[i] = 1; b--;}
        }
    }
    if (a != 0 || b != 0) {cout << "-1" << '\n'; return 0;}
    for (int i = 0; i < 60; i++) 
        if (bit_C[i] == 0)
            if (cnt) {
                bit_A[i] = 1; bit_B[i] = 1; cnt--;
            }
    if (cnt) {cout << "-1" << '\n'; return 0;}
    cout << bit_A.to_ullong() << ' ' << bit_B.to_ullong() << '\n';
    return 0;
}
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

# E - Set Add Query

# Solution

提前用 set<int> 模拟出第 iii 个询问后的 SSS 的大小,设为 siz[i]siz[i]siz[i]

对于每个数,答案就是,奇数次出现 ∼\sim∼ 偶数次出现的 sizsizsiz 的和

直接用树状数组或前缀和就可以快速得到

如果最后一个是奇数次,那么就加上个点到最后所有的 sizsizsiz

# Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
    freopen ("E.in","r",stdin);
    int n, Q; cin >> n >> Q;
    vector<int> q(Q + 1, 0);
    for (int i = 1; i <= Q; i ++)
        cin >> q[i];
    set<int> st;
    vector<ll> cnt(Q + 1, 0);
    for (int i = 1; i <= Q; i++) {
        int x = q[i];
        if (st.count(x)) 
            st.erase(x);
        else
            st.insert(x);
        cnt[i] = st.size();
    }
    
    vector<vector<int> > pos(n + 1, vector<int>());
    for (int i = 1; i <= Q; i++)
        pos[q[i]].push_back(i);
    
    vector<ll> c(Q + 1, 0);
    auto add = [&](int x, int v) {
        for (; x <= Q; x += x & -x)
            c[x] += v;
    };
    auto query = [&](int x) {
        ll res = 0;
        for (; x; x -= x & -x)
            res += c[x];
        return res;
    };

    for (int i = 1; i <= Q; i++) 
        add(i, cnt[i]);
    
    for (int i = 1; i <= n; i++) {
        ll ans = 0;
        if (pos[i].size() & 1)
            pos[i].push_back(Q + 1);
        
        for (int j = 0; j < pos[i].size(); j += 2) {
            ans += query(pos[i][j + 1] - 1) - query(pos[i][j] - 1);
        }
        cout << ans << ' ';
    }
    return 0;
}
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

# F - Non-overlapping Squares

# Solution

先考虑两个 M×MM\times MM×M 正方形的情况

显然,存在一条线,把 N×NN\times NN×N 的正方形切成两个矩形,M×MM\times MM×M 的正方形分别在一个矩形中

所以推广到三个 M×MM\times MM×M 的正方形,就会出现 666 种情况:

image.png

每个 M×MM\times MM×M 的正方形必然分别在一个矩形中

我们先求出每个点为右下角作的点的 M×MM\times MM×M 的区间和

然后对于每个单独的矩形块,只需要找矩形内的最大值即可

可以使用线段树套线段树解决,时间复杂度为 O(n2log⁡2n)O(n^2\log ^2 n)O(n2log2n)

我在具体实现时只写了三个,然后通过旋图去求另外三个

# Code

#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = 1e17;
const int maxn = 1e3 + 1;

ll maxv[maxn << 2][maxn << 2];

struct Segment_Tree {
    int n;

    void build_y (int u, int rt, int l, int r) {
        maxv[rt][u] = -inf;
        if (l == r) return;
        int mid = (l + r) >> 1;
        build_y(u << 1, rt, l, mid);
        build_y(u << 1 | 1, rt, mid + 1, r);
    }

    void build_x (int u, int l, int r) {
        build_y (1, u, 1, n);
        if (l == r) return;
        int mid = (l + r) >> 1;
        build_x(u << 1, l, mid);
        build_x(u << 1 | 1, mid + 1, r);
    }

    void init(int _n) {
        n = _n;
        build_x(1, 1, n);
    }

    void update_y(int u, int rt, int l, int r, int y, ll val) {
        if (l == r) {
            maxv[rt][u] = max(maxv[rt][u], val);
            return;
        }
        int mid = (l + r) >> 1;
        if (y <= mid) update_y(u << 1, rt, l, mid, y, val);
        else update_y(u << 1 | 1, rt, mid + 1, r, y, val);
        maxv[rt][u] = max(maxv[rt][u << 1], maxv[rt][u << 1 | 1]);
    }
    void update_x(int u, int l, int r, int x, int y, ll val) {
        update_y(1, u, 1, n, y, val);
        if (l == r) return;
        int mid = (l + r) >> 1;
        if (x <= mid) update_x(u << 1, l, mid, x, y, val);
        else update_x(u << 1 | 1, mid + 1, r, x, y, val);
    }
    ll query_y (int u, int rt, int l, int r, int ql, int qr) {
        if (ql <= l && r <= qr)
            return maxv[rt][u];
        int mid = (l + r) >> 1;
        ll res = -inf;
        if (ql <= mid)
            res = max(res, query_y(u << 1, rt, l, mid, ql, qr));
        if (qr > mid)
            res = max(res, query_y(u << 1 | 1, rt, mid + 1, r, ql, qr));
        return res;
    }
    ll query_x (int u, int l, int r, int qlx, int qrx, int qly, int qry) {
        if (qlx <= l && r <= qrx)
            return query_y (1, u, 1, n, qly, qry);
        int mid = (l + r) >> 1;
        ll res = -inf;
        if (qlx <= mid)
            res = max(res, query_x(u << 1, l, mid, qlx, qrx, qly, qry));
        if (qrx > mid)
            res = max(res, query_x(u << 1 | 1, mid + 1, r, qlx, qrx, qly, qry));
        return res;
    }
}st;

ll sum[maxn][maxn], a[maxn][maxn];

ll solve (int n, int m, vector<vector<ll> >& mp) {
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            sum[i][j] = sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1] + mp[i][j]; // 2维前缀和
        
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) {
            if (i < m || j < m) { a[i][j] = -inf; continue; }
            a[i][j] = sum[i][j] - sum[i - m][j] - sum[i][j - m] + sum[i - m][j - m]; // 2维区间和
        }
    
    st.init(n);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            st.update_x(1, 1, n, i, j, a[i][j]);
    
    ll ans = -inf, now_1, now_2, now_3;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) {
            if (i >= m && j - i >= m && n - j >= m) {
                now_1 = st.query_x (1, 1, n, 1, n, 1, i);
                now_2 = st.query_x (1, 1, n, 1, n, i + m, j);
                now_3 = st.query_x (1, 1, n, 1, n, j + m, n);
                ans = max(ans, now_1 + now_2 + now_3);
            }

            if (i >= m && j >= m && n - j >= m && n - i >= m) {
                now_1 = st.query_x (1, 1, n, 1, i, 1, n);
                now_2 = st.query_x (1, 1, n, i + m, n, 1, j);
                now_3 = st.query_x (1, 1, n, i + m, n, j + m, n);
                ans = max(ans, now_1 + now_2 + now_3);
            }

            if (i >= m && n - i >= m && j >= m && n - j >= m) {
                now_1 = st.query_x (1, 1, n, i + m, n, 1, n);
                now_2 = st.query_x (1, 1, n, 1, i, 1, j);
                now_3 = st.query_x (1, 1, n, 1, i, j + m, n);
                ans = max(ans, now_1 + now_2 + now_3);
            }
        }
    return ans;
}

inline ll read_ll() {
    ll x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

inline int read_int() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int main() {
    freopen ("F.in", "r", stdin);
    freopen ("F.out", "w", stdout);
    int n, m; n = read_int(); m = read_int();
    vector<vector<ll> > mp(n + 1, vector<ll>(n + 1, 0)), sum(n + 1, vector<ll>(n + 1, 0));
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            mp[i][j] = read_ll();
    
    ll ans = solve(n, m, mp);
    for (int k = 0; k < 1; k++) {
        // rotate 90 degree
        vector<vector<ll> > tmp(n + 1, vector<ll>(n + 1, 0));
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                tmp[i][j] = mp[j][n - i + 1];
        mp = tmp;
        ans = max(ans, solve(n, m, mp));
    }
    cout << ans << endl;
    return 0;
}

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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156

# G - Grid Coloring 2

# Solution

非常有意思一个题

我们利用拆点的思想,把问题转化成一个网络流模型

把一个点拆成 4 个点 1,2,3,41,2,3,41,2,3,4

然后建立一个超级原点 SSS 和一个超级汇点 TTT

考虑这样建边:

(1) 对于每个点 (x,y)(x,y)(x,y)

建边 (x,y)k+1→(x,y)k(1≤k<4)(x,y)_{k+1} \rightarrow (x,y)_{k}(1\le k < 4)(x,y)k+1​→(x,y)k​(1≤k<4)

image.png

这保证了, T→T\rightarrowT→ Bx,yB_{x,y}Bx,y​ 的完整性

(2) 对于一个点 Ax,y=pA_{x,y} = pAx,y​=p,

如果 p>1p>1p>1 建边 S→(x,y)p−1S\rightarrow (x,y)_{p-1}S→(x,y)p−1​,

如果 p<5p<5p<5 建边 (x,y)p→T(x,y)_{p}\rightarrow T(x,y)p​→T

image.png

这保证了,(x,y)(x,y)(x,y) 只能在 Ax,yA_{x,y}Ax,y​ 处分断

(3) 对于与 (x,y)(x,y)(x,y) 相邻的点 (x′,y′)(x',y')(x′,y′)

建边 (x,y)k→(x′,y′)k(x,y)_{k}\rightarrow (x',y')_{k}(x,y)k​→(x′,y′)k​ ,边权为 111

建边 (x,y)k→(x′,y′)v(1≤v<k)(x,y)_k\rightarrow (x',y')_v\ (1\le v < k)(x,y)k​→(x′,y′)v​ (1≤v<k) ,边权为 222

image.png

这里用到了一个很妙的性质,1+3+⋯+(2k+1)=k21+3+\cdots+(2k+1)=k^21+3+⋯+(2k+1)=k2

此时 Bx=4,By=1B_x = 4,B_y=1Bx​=4,By​=1

则所需要的代价就是 1+(2+1)+(2+2+1)=91+(2+1)+(2+2+1)=91+(2+1)+(2+2+1)=9

如果 Bx,ByB_x,B_yBx​,By​ 相差 kkk,那么就按照上面的公式这样累加,来实现平方数

则 S−TS-TS−T 最小割就是答案

# Code

#include <bits/stdc++.h>
#include <atcoder/maxflow>
using namespace std;

const int inf = 0x3f3f3f3f;

int main() {
    int n; cin >> n;
    atcoder::mf_graph<int> g(1 + 4 * n * n + 1);
    const int S = 0, T = 1 + 4 * n * n;
    const auto id = [&] (int x, int y, int v) {
        return (x * n + y) * 4 + v;
    };
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            for (int v = 1; v + 1 <= 4; v++) 
                g.add_edge(id(i, j, v + 1), id(i, j, v), inf);
    
    for (int i = 0; i < n; i++)
        for (int j = 0; j + 1 < n; j++)
            for (int v = 1; v <= 4; v++) {
                g.add_edge(id(i, j, v), id(i, j + 1, v), 1);
                g.add_edge(id(i, j + 1, v), id(i, j, v), 1);
                for (int u = 1; u < v; u++) {
                    g.add_edge(id(i, j, v), id(i, j + 1, u), 2);
                    g.add_edge(id(i, j + 1, v), id(i, j, u), 2);
                }
            }
    
    for (int i = 0; i + 1 < n; i++)
        for (int j = 0; j < n; j++)
            for (int v = 1; v <= 4; v++) {
                g.add_edge(id(i, j, v), id(i + 1, j, v), 1);
                g.add_edge(id(i + 1, j, v), id(i, j, v), 1);
                for (int u = 1; u < v; u++) {
                    g.add_edge(id(i, j, v), id(i + 1, j, u), 2);
                    g.add_edge(id(i + 1, j, v), id(i, j, u), 2);
                }
            }

    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++) {
            int x; cin >> x;
            if (x == 0) continue;
            if (x < 5)
                g.add_edge(id(i, j, x), T, inf);
            if (x > 1) 
                g.add_edge(S, id(i, j, x - 1), inf);
        }
    
    g.flow(S, T);
    const auto &cut = g.min_cut(S);
    
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            int x = 1;
            for (int v = 1; v <= 4; v++)
                x += cut[id(i, j, v)];
            cout << x << ' ';
        }
        cout << '\n';
    }
    return 0;
}
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
上次更新: 2025/04/08, 18:03:31
AtCoder Beginner Contest 346 A-G 题解
AtCoder Beginner Contest 353 A-G 题解

← AtCoder Beginner Contest 346 A-G 题解 AtCoder Beginner Contest 353 A-G 题解→

最近更新
01
Java基础语法
05-26
02
开发环境配置
05-26
03
pink 老师 JavaScript 学习笔记
05-26
更多文章>
Theme by Vdoing | Copyright © 2024-2025 Martian148 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式