Codeforces Round #595 (Div. 3)

Codeforces Round #595 (Div. 3)

Posted by hongzhiyin on October 23, 2019

Codeforces Round #595 (Div. 3)

A. Yet Another Dividing into Teams

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const int N = (int)1e2+7;
 
// ------- 变量 ------- //
 
int n, a[N];
 
// ------- 函数 ------- //
 
void Init() {
    scanf("%d", &n);
    rep(i, 0, n) scanf("%d", a+i);
    sort(a, a + n);
}
 
int Solve() {
    rep(i, 1, n) if (a[i] == a[i-1] + 1) return puts("2");
    return puts("1");
}

B. Books Exchange

代码

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
const int N = (int)2e5+7;
 
// ------- 变量 ------- //
 
int n, p[N], vis[N];
 
// ------- 函数 ------- //
 
int dfs(int x, int t) {
    vis[x] = 1;
    if (vis[p[x]]) return vis[x] = t;
    return vis[x] = dfs(p[x], t+1);
}
 
void Init() {
    rep(i, 1, n+1) vis[i] = 0;
    scanf("%d", &n);
    rep(i, 1, n+1) scanf("%d", p+i);
}
 
int Solve() {
    rep(i, 1, n+1) if (!vis[i]) {
        dfs(i, 1);
    }
    rep(i, 1, n+1) printf("%d%c", vis[i], " \n"[i==n]);
    return 0;
}

C. Good Numbers

题意

求大于等于 $n$ 的最小的 $m$ 使得 $m$ 只能用若干互不相同的 $3$ 的幂次相加表示。

即 $m$ 在 $3$ 进制表示下系数只有 $0$ 和 $1$

$1 \le n \le 10^{18}$

题解

  • 预处理出所有 $3$ 的幂次 $pw[\ ]$
  • 二分找到第一个大于等于 $n$ 的 $pw[i]$
  • $ans$ 暂定为 $pw[i]$
  • 大于 $pw[i]$ 的 $pw[\ ]$ 距离 $n$ 更远,显然不能作为答案
  • 但 $ans$ 可能由若干小于 $pw[i]$ 的 $pw[\ ]$ 组成
  • 于是从 $pw[i-1]$ 开始累加 $tmp$
  • 若当前 $tmp < n$ ,则说明当前累加的 $pw[\ ]$ 都是必要的
  • 因为 $\sum_{j=0}^{i-1}pw[j] < pw[i]$
  • 若当前 $tmp >= n$ ,则记录可能的答案
  • 并减去最近累加的 $pw[\ ]$ ,重复上述过程

代码

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
// ------- 变量 ------- //
 
ll n, pw[50];
 
// ------- 函数 ------- //
 
void Pre() {
    ll base = 1;
    rep(i, 0, 40) {
        pw[i] = base;
        base *= 3;
    }
}
 
void Init() {
    scanf("%I64d", &n);
}
 
int Solve() {
    int pos = lower_bound(pw, pw + 40, n) - pw;
    ll ans = pw[pos], tmp = 0;
    while (1) {
        pos--;
        if (pos < 0) break;
        tmp += pw[pos];
        if (tmp >= n) ans = min(ans, tmp), tmp -= pw[pos];
    }
    return printf("%I64d\n", ans);
}

D. Too Many Segments

数据结构 贪心

题意

$n$ 条线段,令数轴上的点不被超过 $k$ 条线段覆盖,最少需删除几条线段。

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

$1 \le l_i \le r_i \le 2 \cdot 10^5$

题解

  • 从左往右扫描数轴上的点
  • 若当前点不满足条件,则需要移除线段
  • 贪心策略:
    • 覆盖当前点的线段中
    • 右端点最靠右边的线段,可能影响的点数最多
    • 因此优先移除
  • 可用优先队列维护
  • 技巧: 对于每条线段,记录左端点和右端点 + 1

代码

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

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

int n, k;
vector <pii> vl[N];
int vr[N];

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

void Init() {
    scanf("%d%d", &n, &k);
    memset(vr, 0, sizeof(vr));
    rep(i, 1, N) vl[i].clear();
    rep(i, 0, n) {
        int l, r; scanf("%d%d", &l, &r);
        vl[l].pb(mp(r, i + 1));
        vr[r+1]++;
    }
}

int Solve() {
    vi ans;
    priority_queue <pii> Q;
    int cur = 0;
    rep(i, 1, N) {
        cur -= vr[i];
        for (auto o : vl[i]) {
            cur++;
            Q.push(o);
        }
        while (cur > k) {
            cur--;
            ans.pb(Q.top().se);
            vr[Q.top().fi+1]--;
            Q.pop();
        }
    }
    printf("%d\n", sz(ans));
    rep(i, 0, sz(ans)) printf("%d%c", ans[i], " \n"[i==sz(ans)-1]);
    return 0;
}

E. By Elevator or Stairs?

dp

题意

$n$ 层楼,给出每层楼之间走楼梯 $a[\ ]$ 和乘电梯的代价 $b[\ ]$ 以及电梯启动的代价 $c$ ,求,从 $1$ 层到每一层的最短时间。

题解

$dp[i][0]$ 表示从第 $i-1$ 层走楼梯到第 $i$ 层的最短时间

$dp[i][1]$ 表示从第 $i-1$ 层乘电梯到第 $i$ 层的最短时间

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const int N = (int)2e5+7;
 
// ------- 变量 ------- //
 
int n, c, a[N], b[N], ans[N][2];
 
// ------- 函数 ------- //
 
void Init() {
    scanf("%d%d", &n, &c);
    rep(i, 1, n) scanf("%d", a+i);
    rep(i, 1, n) scanf("%d", b+i);
}
 
int Solve() {
    ans[1][0] = 0; ans[1][1] = INF;
    rep(i, 2, n+1) {
        ans[i][0] = min(ans[i-1][0], ans[i-1][1]) + a[i-1];
        ans[i][1] = min(ans[i-1][0] + c + b[i-1], ans[i-1][1] + b[i-1]);
    }
    rep(i, 1, n+1) printf("%d%c", min(ans[i][0], ans[i][1]), " \n"[i==n]);
    return 0;
}

F. Maximum Weight Subset

dp

题意

$n$ 个节点的树,点权 $a[\ ]$ ,求最大点权和的节点子集,且子集内节点之间的距离大于 $k$ 。

$1 \le n,k \le 200$

$1 \le a_i \le 10^5$

题解

$dp[u][dep]$ 表示

  • 在以 $u$ 为根的子树中,
  • 选择满足约束条件的节点子集,所能获得的最大点权和,
  • 且子集中所有节点距离根节点至少有 $dep$ 距离

设树根为 $1$ ,则答案为 $dp[1][0]$

设当前根节点为 $u$ ,距离限制为 $dep$ ,则

  • 当 $dep = 0$ 时

    $dp[u][dep] = a[u] + dp[v][k]$

  • 当 $dep \neq 0$ 时

    $dp[u][dep] = max{\ dp[v][dep-1] + \sum dp[other][max(dep-1, k-dep)]\ }$

此时 $dp$ 值表示的是恰好距离为 $dep$ ,因此,求前缀最值

1
per(dep, 0, k+1) dp[u][dep] = dp[v][dep+1];

代码

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
const int N = (int)2e3+7;

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

int n, k, a[N], dp[N][N], pre[N], suf[N];
vi _e[N], e[N];

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

void _dfs(int u, int f = 0) {
    for (auto v : _e[u]) if (v != f) {
        e[u].pb(v);
        _dfs(v, u);
    }
}

void dfs(int u) {
    for (auto v : e[u]) dfs(v);
    pre[0] = suf[sz(e[u])+1] = 0;
    per(dep, 0, k+1) {
        if (dep == 0) {
            dp[u][dep] = a[u];
            for (auto v : e[u]) dp[u][dep] += dp[v][k];
        } else {
            rep(i, 0, sz(e[u])) {
                int v = e[u][i];
                pre[i+1] = pre[i] + dp[v][max(dep-1, k-dep)];
            }
            per(i, 0, sz(e[u])) {
                int v = e[u][i];
                suf[i+1] = suf[i+2] + dp[v][max(dep-1, k-dep)];
            }
            dp[u][dep] = 0;
            rep(i, 0, sz(e[u])) {
                int v = e[u][i];
                dp[u][dep] = max(dp[u][dep], dp[v][dep-1] + suf[i+2] + pre[i]);
            }
        }
        if (dep < k) dp[u][dep] = max(dp[u][dep], dp[u][dep+1]);
    }
}

void Init() {
    scanf("%d%d", &n, &k);
    rep(i, 1, n+1) _e[i].clear(), e[i].clear();
    rep(i, 1, n+1) scanf("%d", a+i);
    rep(i, 1, n) {
        int u, v; scanf("%d%d", &u, &v);
        _e[u].pb(v); _e[v].pb(u);
    }
    _dfs(1);
}

int Solve() {
    dfs(1);
    return printf("%d\n", dp[1][0]);
}