• Codeforces Round #453 (Div. 1)


    C. Bipartite Segments

    分析

    在题目所给出的无向图中只能存在奇数长度的环,说明任意两个环一定无公共边,否则就会出现偶数长度的环。Tarjan 算法找环。
    然后预处理 (d[i]) 表示从 (i) 开始向右最远延伸到的位置,即 ([i, d[i] + 1])这个区间就是不合法的,预处理后缀最小值即可。可以发现 (d) 呈单调不递减,对于每次询问 ((l,r)) 可以二分找到 (d[i]geq r)的最小下标,考虑左右两边算下贡献即可。

    code

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 6e5 + 10;
    int n, m, q;
    struct Edge {
        int to, next;
    } e[N];
    int cnt, head[N];
    void addedge(int u, int v) {
        e[cnt].to = v;
        e[cnt].next = head[u];
        head[u] = cnt++;
    }
    int sz, dfn[N], low[N], vis[N], rht[N];
    long long sum[N];
    stack<int> sta;
    void tarjan(int u, int fa) {
        dfn[u] = low[u] = ++sz;
        vis[u] = 1;
        sta.push(u);
        for (int i = head[u]; ~i; i = e[i].next) {
            int v = e[i].to;
            if (v != fa) {
                if (!dfn[v]) {
                    tarjan(v, u);
                    low[u] = min(low[u], low[v]);
                } else if (vis[v] && low[u] > dfn[v]) {
                    low[u] = dfn[v];
                }
            }
        }
        if (low[u] == dfn[u]) {
            int mn = 9999999, mx = 0;
            while (1) {
                int id = sta.top();
                sta.pop();
                mn = min(mn, id);
                mx = max(mx, id);
                vis[id] = 0;
                if (id == u) break;
            }
            if (mn != mx) rht[mn] = mx - 1;
        }
    }
    int main() {
        cin >> n >> m;
        cnt = sz = 0;
        memset(head, -1, sizeof head);
        for (int i = 0; i < m; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            addedge(u, v);
            addedge(v, u);
        }
        for (int i = 1; i <= n + 1; i++) {
            rht[i] = n;
        }
        for (int i = 1; i <= n; i++) {
            if (!dfn[i]) tarjan(i, -1);
        }
        for (int i = n; i >= 1; i--) {
            rht[i] = min(rht[i], rht[i + 1]);
            sum[i] = sum[i + 1] + rht[i] - i + 1;
        }
        cin >> q;
        while (q--) {
            int l, r;
            scanf("%d%d", &l, &r);
            int x = lower_bound(rht + l, rht + r + 1, r) - rht;
            cout << 1LL * (r - x + 1) * (r - x + 2) / 2 + sum[l] - sum[x] << endl;
        }
        return 0;
    }
    

    D. Weighting a Tree

    分析

    二分图脑洞好题!

    首先要想到根据二分图分类,然后是构造一棵树(将点权转化为边权)。

    首先如果题目给出的无向图就是一个二分图,那么要想答案存在,二分图左右两边结点的点权之和一定相等,因为此时对于二分图中的每一条边,都对两边的结点同时有贡献。那么对于这个二分图的任意生成树,我们都可以得到一个解。(不在树上的边权值都可以置为 0)
    那如果不是二分图呢?
    也就是说存在构成一条边的两个结点在“二分图”的同一边,如果此时“二分图”两边点权之和不等,那么我们可以改变构成这条边的结点的权值去平衡两边的点权和,然后和上面一样构造出一棵树即可。可以发现,这种情况下答案一定存在。
    如果是二分图,且两边点权之和不等,那么答案就不存在了,因为我们找不到一条边去平衡两边的点权之和(因为此时任意边权都对两边的点权有贡献)。

    code

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5 + 10;
    vector<pair<int, int> > G[N];
    int dep[N], vis[N], U, V, I;
    long long c[N], ans[N];
    void dfs1(int fa, int u) {
        for (auto to : G[u]) {
            int v = to.second;
            if (v != fa) {
                if (!dep[v]) {
                    dep[v] = dep[u] + 1;
                    dfs1(u, v);
                } else if (!((dep[u] - dep[v]) & 1)) {
                    U = u;
                    V = v;
                    I = to.first;
                }
            }
        }
    }
    void dfs2(int u) {
        vis[u] = 1;
        for (auto to : G[u]) {
            int v = to.second;
            if (!vis[v]) {
                dfs2(v);
                ans[to.first] = c[v];
                c[u] -= c[v];
            }
        }
    }
    int main() {
        int n, m;
        cin >> n >> m;
        for (int i = 1; i <= n; i++) {
            cin >> c[i];
        }
        for (int i = 1; i <= m; i++) {
            int u, v;
            cin >> u >> v;
            G[u].push_back(pair<int, int>(i, v));
            G[v].push_back(pair<int, int>(i, u));
        }
        dep[1] = 1;
        dfs1(-1, 1);
        long long suml = 0, sumr = 0;
        for (int i = 1; i <= n; i++) {
            ((dep[i] & 1) ? suml : sumr) += c[i];
        }
        if (suml != sumr && !I) {
            cout << "NO
    ";
            return 0;
        }
        long long x = suml - sumr >> 1;
        ans[I] = (dep[U] & 1) ? x : -x;
        c[U] -= ans[I];
        c[V] -= ans[I];
        dfs2(1);
        cout << "YES
    ";
        for (int i = 1; i <= m; i++) {
            cout << ans[i] << " 
    "[i == m];
        }
        return 0;
    }
    
  • 相关阅读:
    学习心得——day2
    学习心得——day3
    学习心得——day1
    Android JNI so库的开发
    android 删除相册图片并同步到图库
    使用AccessibilityService执行开机自启动
    UDP Server
    uicode编码解码
    GreenDao的使用
    java之并发编程线程池的学习
  • 原文地址:https://www.cnblogs.com/ftae/p/8337951.html
Copyright © 2020-2023  润新知