Codeforces Round #627 (Div. 3)

D. Pair of Topics

题目:https://codeforces.com/contest/1324/problem/D

题解

将不等式变下形:\(a_i + a_j > b_i + b_j\)等价于\(a_i - b_i > b_j - a_j\)
\(c[i] = a_i - b_i, d[j] = b[j] - a[j]\),问题转化为对于每一个\(c\),有多少个\(d\)小于它。将\(d\)排序,然后二分查找即可。

注意去重\(a[i] > b[i]\)的情况

int main() {
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }

    long long ans = 0;
    for (int i = 1; i <= n; ++i) {
        cin >> b[i];
        c[i] = a[i] - b[i];
        if (c[i] > 0) ans--;
        b[i] = b[i] - a[i];

    }

    sort(b + 1, b + n + 1);

    for (int i = 1; i <= n; ++i) {
        ans += lower_bound(b + 1, b + n + 1, c[i]) - (b + 1);
    }
    cout << ans / 2 << endl;
    return 0;
}

E. Sleeping Schedule

题目:https://codeforces.com/contest/1324/problem/E
决定多练练DP,orz

题解

对于每次碎觉,可以选择提前睡或者不提前睡
\(dp[i][j]:=\) 睡觉次数为\(i\),提前睡了\(j\)次的 \(good\) \(sleeping\) \(times\) (很自然的定义,有木有),\(sum[i] = \sum_{j = 1}^{i}a_i\),有:

  • \(dp[i][j]\) = \(max(dp[i - 1][j]\), \(dp[i - 1][j - 1]) + check((sum - j) \% h, l, r)\)

注意边界!

只需要知道提前睡了几次,不用记录是哪几次提前睡的(%%%出题人)。因为模运算可以合并

/*cf给出代码一向都很nice,用vector初始化二维数组是真的方便*/

inline bool check(int x, int l, int r) {
    return (l <= x && x <= r);
}

void Solve() {
    int n, h, l, r;
    cin >> n >> h >> l >> r;

    vector<int> a(n + 1);
    for (int i = 1; i <= n; ++i) cin >> a[i];

    vector<vector<int> > dp(n + 1, vector<int>(n + 1, -INF));
    dp[0][0] = 0;

    int sum = 0;
    for (int i = 1; i <= n; ++i) {
        sum += a[i];
        dp[i][0] = dp[i - 1][0] + check(sum % h, l, r);
        for (int j = 1; j < i; ++j) dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1]) + check((sum - j) % h, l, r);
        dp[i][i] = dp[i - 1][i - 1] + check((sum - i) % h, l, r);
    }

    cout << *max_element(dp[n].begin(), dp[n].end()) << endl;
}

F. Maximum White Subtree

一棵树(无根)有\(n\)个节点,每个节点都被染色(黑色 or 白色)
对于每个节点,找一个包含该节点的连通块,使得连通块中白色节点的个数与黑色节点个数的差值最大,既\(Cnt_{white} -Cnt_{black}\)最大

题解

题目中\(subtree\)指的是包含节点\(u\)的连通块。
假如这是一颗有根树,对于每个节点,只考虑求: 以该节点为根的子树的\(max(Cnt_w - cnt_b)\)。那么自底向上\(dp\)统计即可:
\(dp[u]:=cnt_w - cnt_b\),则\(dp[parent_u] = dp[parent_u] + (dp[u] > 0 ? dp[u] : 0)\)
显然,这样只正确求出了根节点的答案。对于除根以外的节点,都没有考虑\(parent_u\)对节点\(u\)的贡献。
\(ans[u]:=\)节点\(u\)的完整答案,则\(ans[root] =dp[root]\)。假设我们已经求得了\(ans[parent_u]\),怎么求\(ans[u]\)呢?仔细观察一下,发现:

  1. \(dp[u] \leq 0\),意味着\(u\)\(parent_u\)没有贡献
    • \(ans[parent_u] \leq 0\),则 \(ans[u] = dp[u]\)
    • \(ans[parent_u] > 0\),则 \(ans[u] = ans[parent_u] + dp[u]\)
  2. \(dp[u] > 0\)
    • \(ans[parent_u] \leq dp[u]\),则 \(ans[u] = dp[u]\)
    • \(ans[parent_u] > 0\),则 \(ans[u] = ans[parent_u]\)

整理一下,然后\(code\)


int n, cnt;
int dp[MAXN], ans[MAXN], head[MAXN];

struct edge { int to, next; } e[MAXN * 2];

void Inite() {
    cnt = 0;
    memset(head, -1, sizeof(head));
}

void add_edge(int u, int v) {
    e[cnt].to = v;
    e[cnt].next = head[u];
    head[u] = cnt++;
}

void DFS_down_to_top(int u, int p) {
    for (int i = head[u]; i != -1; i = e[i].next) {
        int v = e[i].to;
        if (p == v) continue;
        DFS_down_to_top(v, u);
        dp[u] += (dp[v] > 0 ? dp[v] : 0);
    }
}

void DFS_top_to_down(int u, int p) {
    for (int i = head[u]; i != -1; i = e[i].next) {
        int v = e[i].to;
        if (p == v) continue;
        if (dp[v] <= 0) {
            if (ans[u] <= 0) ans[v] = dp[v];
            else ans[v] = dp[v] + ans[u];
        }
        else {
            if (ans[u] <= dp[v]) ans[v] = dp[v];
            else ans[v] = ans[u];
        }
        DFS_top_to_down(v, u);
    }
}

int main() {

    Inite();

    cin >> n;
    for (int i = 1, u; i <= n; ++i) {
        cin >> u;
        dp[i] = (u == 1 ? 1 : -1);
    }
   
    for (int i = 1, u, v; i < n; ++i) {
        cin >> u >> v;
        add_edge(u, v);
        add_edge(v, u);
    }

    DFS_down_to_top(1, -1);
    ans[1] = dp[1];
    DFS_top_to_down(1, -1);

    for (int i = 1; i <= n; ++i) cout << ans[i] << " ";
    cout << endl;

    return 0;
}


posted @ 2020-03-27 17:54  天之道,利而不害  阅读(203)  评论(0编辑  收藏  举报