B. 愚者
题目描述 :
BEMANI 的军师 STULTI 从潜伏在精灵高层的密探手中得知了神杖的情报,他对奥术宝石中 蕴含的远古神秘力量十分感兴趣。他设计夺取了数块奥术宝石,并带领手下的研究人员全力破解。 经过了一个月的艰苦尝试,终于破译了其内部能量结构。 奥数宝石可以抽象成一个\(n\) 个点与 \(m\) 条边的无向图,点的编号为 \(1 − n\)。其中每个点有一个基 础能量 \(ai\),每个点的基础能量互不相同。整个宝石有一个附加能量 \(b\)。宝石的所有点必须能互相到 达,才能释放出魔力。宝石的魔力值被定义为所有点的基础能量的最小值加上宝石的整体能量,再 乘以宝石中的点数。具体地说,一个 \(n\) 个点的连通图的魔力值为 \((∀i, min\{ai\} + b) ∗ n\)。 同时,宝石的结构是可以修改的。你可以删除宝石中任意个点,得到新的宝石,并可能改变其 魔力值。注意宝石需要是一个连通图才有魔力值。现在 STULTI 手中有一块没有重边的宝石,他会 询问 \(q\) 次,在整个宝石的附加能量 \(b\) 为他询问的值时,经过修改后宝石的魔力值最大可能是多少。 一个没有点的图的魔力值为 \(−10^{10^{10}}\)。
输入格式 :
第一行三个正整数 \(n\), \(m\), \(q\)。
接下来一行 \(n\) 个整数,第 \(i\) 个表示 \(ai\)。
接下来 \(m\) 行每行两个数 \(ui\) , \(vi\) 表示一条连接 \(ui\) , \(vi\) 的边。
接下来 \(q\) 行每行一个整数 \(bi\)。
输出格式 :
输出 \(q\) 行,每行一个整数表示当前最大魔力值。
样例 :
输入
4 2 3
1 2 3 4
1 2
3 4
1000
-1000
0
输出
2006
-996
6
数据范围 :
对于所有数据,\(n, q ≤ 2 ∗ 10^5 , n/2 ≤ m ≤ 3 ∗ 10^5 , m ≤ 2 ∗ n, −10^6 ≤ ai , b ≤ 10^6\)。
Solution :
考虑连通图的魔力值 \((∀i, min\{ai\} + b) ∗ n\) 只与图中最小点的权值和点的个数有关,可以把删点转换为加边,对于所有边按照边连接的两个点的较小 \(ai\) 排序,按顺序加边时更新连通图的 \(size\) 所对应的最小的点权,这样对于每次询问的 \(b\) 只要枚举连通块的大小并计算答案即可。但这样是 \(O(nq)\) 的,不能拿满分。
设 \(Mx[i]\) 为当前得到的大小为 \(i\) 的联通图对应的最小权值,
\(ans = (∀i,max\{i * Mx[i] + i * b\})\),
\(i * Mx[i] = -b * i + ans\)
即 对于每个斜率 \(-b\), 过某一点 \((i, i * Mx[i])\),使它的截距最大为 \(ans\)
可以证明,对于每个斜率,使它截距最大的点一定在上凸包上,且该点与前一点的斜率 > \(-b\) > 该点与后一点的斜率。
像这样...
所以用队列维护一个上凸包,然后将每个询问的 \(b\) 排个序,就可以实现线性回答啦。
Code :
#include<bits/stdc++.h> using namespace std; #define fi first #define se second typedef long long LL; typedef pair<int, int> pii; const int N = 3e5 + 7; const LL inf = 1e10; struct Edge {int u, v, w;} e[N]; pii b[N]; int n, m, q, a[N]; int fa[N], sz[N], mn[N], Q[N]; LL Mx[N], ans[N]; template <class T> void read(T &x) { bool f = false; x = 0; char ch = getchar(); while (ch<'0' || ch>'9') {if (ch == '-') f = true; ch = getchar();} while (ch>='0' && ch<='9') x = x * 10 + ch - '0', ch = getchar(); if (f) x = -x; } inline int father(int x) {return fa[x] == x ? x : fa[x] = father(fa[x]);} inline bool cmp1(Edge x, Edge y) { return x.w > y.w; } inline bool cmp2(pii x, pii y) { return x.fi > y.fi; } inline void ChMax(int &x, int y) { if (x < y) x = y; } inline long double slope(int x, int y) {return 1.0 * (Mx[y] - Mx[x]) / (y - x);} int main() { read(n), read(m), read(q); for (int i = 0; i <= n; i++) Mx[i] = -inf; for (int i = 1; i <= n; i++) { read(a[i]); fa[i] = i, sz[i] = 1, mn[i] = a[i]; Mx[1] = max(Mx[1], 1ll * mn[i]); } for (int i = 1; i <= m; i++) { read(e[i].u), read(e[i].v); e[i].w = min(a[e[i].u], a[e[i].v]); } sort(e + 1, e + 1 + m, cmp1); for (int i = 1; i <= m; i++) { int u = e[i].u, v = e[i].v; int f1 = father(u), f2 = father(v); if (f1 != f2) { fa[f2] = f1; sz[f1] += sz[f2]; mn[f1] = min(mn[f1], mn[f2]); Mx[sz[f1]] = max(Mx[sz[f1]], 1ll * mn[f1]); } } for (int i = n - 1; i >= 1; i--) Mx[i] = max(Mx[i], Mx[i + 1]); for (int i = 1; i <= n; i++) Mx[i] = Mx[i] * i; int h = 1, t = 0; for (int i = 1; i <= n; i++) { while (h < t && slope(Q[t - 1], Q[t]) <= slope(Q[t], i)) t--; Q[++t] = i; } for (int i = 1; i <= q; i++) { read(b[i].fi); b[i].fi = -b[i].fi; b[i].se = i; } sort(b + 1, b + 1 + q, cmp2); for (int i = 1; i <= q; i++) { while (h < t && slope(Q[h], Q[h + 1]) > b[i].fi) h++; ans[b[i].se] = Mx[Q[h]] - 1ll * Q[h] * b[i].fi; } for (int i = 1; i <= q; i++) { printf("%lld\n", ans[i]); } return 0; }