Codeforces Round #453 (Div. 1)

C. Bipartite Segments

分析

在題目所給出的無向圖中只能存在奇數長度的環,說明任意兩個環必定無公共邊,不然就會出現偶數長度的環。Tarjan 算法找環。
而後預處理 \(d[i]\) 表示從 \(i\) 開始向右最遠延伸到的位置,即 \([i, d[i] + 1]\)這個區間就是不合法的,預處理後綴最小值便可。能夠發現 \(d\) 呈單調不遞減,對於每次詢問 \((l,r)\) 能夠二分找到 \(d[i]\geq r\)的最小下標,考慮左右兩邊算下貢獻便可。c++

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

分析

二分圖腦洞好題!算法

首先要想到根據二分圖分類,而後是構造一棵樹(將點權轉化爲邊權)。spa

首先若是題目給出的無向圖就是一個二分圖,那麼要想答案存在,二分圖左右兩邊結點的點權之和必定相等,由於此時對於二分圖中的每一條邊,都對兩邊的結點同時有貢獻。那麼對於這個二分圖的任意生成樹,咱們均可以獲得一個解。(不在樹上的邊權值均可以置爲 0)
那若是不是二分圖呢?
也就是說存在構成一條邊的兩個結點在「二分圖」的同一邊,若是此時「二分圖」兩邊點權之和不等,那麼咱們能夠改變構成這條邊的結點的權值去平衡兩邊的點權和,而後和上面同樣構造出一棵樹便可。能夠發現,這種狀況下答案必定存在。
若是是二分圖,且兩邊點權之和不等,那麼答案就不存在了,由於咱們找不到一條邊去平衡兩邊的點權之和(由於此時任意邊權都對兩邊的點權有貢獻)。code

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\n";
        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\n";
    for (int i = 1; i <= m; i++) {
        cout << ans[i] << " \n"[i == m];
    }
    return 0;
}
相關文章
相關標籤/搜索