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;
}