在題目所給出的無向圖中只能存在奇數長度的環,說明任意兩個環必定無公共邊,不然就會出現偶數長度的環。Tarjan 算法找環。
而後預處理 \(d[i]\) 表示從 \(i\) 開始向右最遠延伸到的位置,即 \([i, d[i] + 1]\)這個區間就是不合法的,預處理後綴最小值便可。能夠發現 \(d\) 呈單調不遞減,對於每次詢問 \((l,r)\) 能夠二分找到 \(d[i]\geq r\)的最小下標,考慮左右兩邊算下貢獻便可。c++
#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; }
二分圖腦洞好題!算法
首先要想到根據二分圖分類,而後是構造一棵樹(將點權轉化爲邊權)。spa
首先若是題目給出的無向圖就是一個二分圖,那麼要想答案存在,二分圖左右兩邊結點的點權之和必定相等,由於此時對於二分圖中的每一條邊,都對兩邊的結點同時有貢獻。那麼對於這個二分圖的任意生成樹,咱們均可以獲得一個解。(不在樹上的邊權值均可以置爲 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\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; }