Codeforces Round #544 (Div. 3)

Codeforces Round #544 (Div. 3)

Posted by hongzhiyin on October 22, 2019

Codeforces Round #544 (Div. 3)

A. Middle of the Contest

题意

求两个时间段的中点。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* <<head>> */ 
// ------- 变量 ------- //
 
int h1, m1, h2, m2;
 
// ------- 函数 ------- //
 
void Init() {
    scanf("%d:%d%d:%d", &h1, &m1, &h2, &m2);
}
 
int Solve() {
    int a = h1 * 60 + m1;
    int b = h2 * 60 + m2;
    int c = b - a >> 1;
    a += c;
    return printf("%02d:%02d\n", a/60, a%60);
}

B. Preparation for International Women’s Day

题意

$n$ 个数,最多能分多少组,使得每组两个数,且两个数相加可被 $k$ 整除。

代码

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
/* <<head>> */
const int N = (int)1e5+7;

// ------- 变量 ------- //

int n, k;
int cnt[107];

// ------- 函数 ------- //

void Init() {
    scanf("%d%d", &n, &k);
    rep(i, 0, n) {
        int x; scanf("%d", &x);
        cnt[x%k]++;
    }
}

int Solve() {
    ll ans = 0;
    rep(i, 1, (k+1)/2) ans += min(cnt[i], cnt[k-i]);
    if (~k & 1) ans += cnt[k/2] / 2;
    ans += cnt[0] / 2;
    return printf("%lld\n", ans << 1);
}

C. Balanced Team

题意

$n$ 个数,选尽可能多的数,使得两两差值不超过 $5$ 。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* <<head>> */
const int N = (int)2e5+7;

// ------- 变量 ------- //

int n, a[N];

// ------- 函数 ------- //

void Init() {
    scanf("%d", &n);
    rep(i, 0, n) scanf("%d", a+i);
}

int Solve() {
    sort(a, a + n);
    int ans = -INF, r = 1;
    rep(l, 0, n) {
        while (r < n && a[r] - a[l] <= 5) r++;
        ans = max(ans, r - l);
    }
    return printf("%d\n", ans);
}

D. Zero Quantity Maximization

题意

$n$ 个数 $a[\ ]$ 和 $n$ 个数 $b[\ ]$ ,令 $c[i] = d \cdot a[i] + b[i]$ ,求:当选择最优的 $d$ 时, $c[\ ]$ 中最多有多少个 $0$ 。

题解

推导出 $\large d = -\frac{b[i]}{a[i]}$ ,用 $map$ 记录。

特判 $a[i] = 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
/* <<head>> */
const int N = (int)2e5+7;

// ------- 变量 ------- //

int n, a[N], b[N];
map <pii, int> M;

// ------- 函数 ------- //

pii work(int a, int b) {
    int g = __gcd(a, b);
    a /= g; b /= g;
    if (a < 0) return mp(-a, -b);
    return mp(a, b);
}

void Init() {
    scanf("%d", &n);
    
    M.clear();
    rep(i, 1, n+1) scanf("%d", a+i);
    rep(i, 1, n+1) scanf("%d", b+i);
}

int Solve() {
    int ans = 0, num = 0;
    rep(i, 1, n+1) num += (a[i] == 0 && b[i] == 0);
    rep(i, 1, n+1) {
        if (a[i] == 0) continue;
        int tmp = ++M[work(a[i], b[i])];
        ans = max(ans, tmp);
    }
    return printf("%d\n", ans + num);
}

E. K Balanced Teams

dp

题意

$n$ 个数分 $k$ 组,每组内极差不超过 $5$ ,求最多有多少个数可被分到组内。

$1 \le k \le n \le 5000$

$1 \le a_i \le 10^9$

题解

  • 从小到大排序,分组一定为若干段。

  • 预处理 $pre[i]$ 表示第 $i$ 个数向左最长延伸多长。

  • $dp[i][j]$ 表示前 $i$ 个数,分 $j$ 组的答案,转移有:

    1. 不使用第 $j$ 个数:

      $dp[i][j] = dp[i-1][j]$

    2. 以第 $j$ 个数作为最后一组的最后一个数:

      $dp[i][j] = dp[i-pre[i]][j-1] + pre[i]$

  • 边界情况: $i-pre[i]$ 可能会小于 $j-1$

  • 当 $dp[i][j]$ 的 $i$ 小于 $j$ 时,答案即为 $i$

代码

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
/* <<head>> */
const int N = (int)5e3+7;
 
// ------- 变量 ------- //
 
int n, k, a[N], pre[N], dp[N][N];
 
// ------- 函数 ------- //
 
void Init() {
    scanf("%d%d", &n, &k);
    memset(dp, 0, sizeof(dp));
 
    rep(i, 1, n+1) scanf("%d", a+i);
    sort(a + 1, a + n + 1);
}
 
int Solve() {
    int l = 1;
    rep(r, 1, n+1) {
        while (a[r] - a[l] > 5) l++;
        pre[r] = r - l + 1;
    }
    rep(i, 1, n+1) rep(j, 1, k+1) {
        if (i <= j) dp[i][j] = i;
        else dp[i][j] = max(dp[i-1][j], dp[i-pre[i]][j-1] + pre[i]);
    }
    return printf("%d\n", dp[n][k]);
}

F1. Spanning Tree with Maximum Degree

题意

$n$ 个点, $m$ 条边,求一棵生成树,使得度数最大的节点的度数尽可能大。

题解

贪心

代码

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
/* <<head>> */
const int N = (int)2e5+7;

// ------- 变量 ------- //

int n, m;
vi e[N];
vector <pii> ans;

// ------- 函数 ------- //

int vis[N];
void bfs(int rt) {
    queue <int> Q;
    Q.push(rt);
    vis[rt] = 1;
    while (!Q.empty()) {
        int u = Q.front(); Q.pop();
        for (auto v : e[u]) if (!vis[v]) {
            ans.pb(mp(u, v));
            Q.push(v);
            vis[v] = 1;
        }
    }
}

void Init() {
    scanf("%d%d", &n, &m);
    ans.clear();
    rep(i, 0, n+1) e[i].clear();
    memset(vis, 0, sizeof(vis));

    rep(i, 0, m) {
        int u, v; scanf("%d%d", &u, &v);
        e[u].pb(v); e[v].pb(u);
    }
}

int Solve() {
    int mx = 0, rt;
    rep(i, 1, n+1) if (sz(e[i]) > mx) {
        mx = sz(e[i]); rt = i;
    }
    bfs(rt);
    for (auto o : ans) printf("%d %d\n", o.fi, o.se);
}

F2. Spanning Tree with One Fixed Degree

图论 生成树 并查集

题意

$n$ 个点, $m$ 条边的无向无权连通图,保证无自环和重边。

构造一棵生成树,满足编号为 $1$ 的节点有 $D$ 个度。

$2 \le n \le 2 \cdot 10^5$

$n-1 \le m \le min(2 \cdot 10^5, \frac{n(n-1)}{2})$

$1 < D < n$

题解

  • 若 $1$ 号节点度数小于 $D$ ,则无解
  • 去掉 $1$ 号节点及所连的边,然后将剩下的点用并查集缩点
  • 若连通块个数大于 $D$ ,则无解
  • 先将 $1$ 号节点与连通块连边
  • 多余的度数随便选择边与连通块相连
  • 剩下的边用 $bfs$ 构造

代码

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
/* <<head>> */
const int N = (int)2e5+7;

// ------- 变量 ------- //

int n, m, D;
int fa[N], ucc[N], vis_u[N], vis[N];
vi e[N];

// ------- 函数 ------- //

int Find(int x) { return x == fa[x] ? x : fa[x] = Find(fa[x]); }
void Union(int x, int y) { fa[Find(y)] = Find(x); }

void Init() {
    scanf("%d%d%d", &n, &m, &D);

    memset(vis, 0, sizeof(vis));
    memset(vis_u, 0, sizeof(vis_u));
    rep(i, 1, n+1) e[i].clear();
    rep(i, 1, n+1) fa[i] = i;
    rep(i, 0, m) {
        int u, v; scanf("%d%d", &u, &v);
        e[u].pb(v); e[v].pb(u);
        if (u != 1 && v != 1) Union(u, v);
    }
}

int Solve() {
    if (sz(e[1]) < D) return puts("NO");
    rep(i, 2, n+1) ucc[i] = Find(i);
    int cnt = 0;
    rep(i, 2, n+1) if (ucc[i] == i) cnt++;
    if (cnt > D) return puts("NO");

    vector <pii> ans;
    queue <int> Q;
    vis[1] = 1;
    for (auto v : e[1]) if (!vis_u[ucc[v]]) {
        vis_u[ucc[v]] = 1;
        ans.pb(mp(1, v));
        vis[v] = 1;
        Q.push(v);
    }
    int d = D - cnt;
    for (auto v : e[1]) if (!vis[v]) {
        if (d == 0) break;
        d--;
        ans.pb(mp(1, v));
        vis[v] = 1;
        Q.push(v);
    }

    while (!Q.empty()) {
        int u = Q.front(); Q.pop();
        for (auto v : e[u]) if (!vis[v]) {
            vis[v] = 1;
            ans.pb(mp(u, v));
            Q.push(v);
        }
    }

    puts("YES");
    for (auto o : ans) printf("%d %d\n", o.fi, o.se);
    return 0;
}