[nowcoder_Wannafly挑战赛4_F]线路规划
试题描述
Q国的监察院是一个神秘的组织。
这个组织掌握了整个帝国的地下力量,监察着Q国的每一个人。
监察院一共有 \(N\) 个成员,每一个成员都有且仅有 \(1\) 个直接上司,而他只听从其上直接司的命令。其中 \(1\) 号成员是监察院的院长,这个庞然大物的主人。
由于时代的进步,监察院议会决定升级组织的旧式通信器,安装最新的反侦测通信器。
他们拿出了 \(M\) 组线路方案,其中第i组线路方案可以用一个四元组 \((x[i]、y[i]、k[i]、w[i])\) 描述,表示第 \(x[i]\) 号成员可以安装与 \(y[i]\) 号成员的直接通信线路,费用为 \(w[i]\);\(x[i]\) 号成员的上司可以安装与 \(y[i]\) 号成员的上司的直接通信线路,费用为 \(w[i]\);\(x[i]\) 号成员的上司的上司可以安装与 \(y[i]\) 号成员的上司的上司的直接通信线路,费用为 \(w[i]\); …… ;\(x[i]\) 号成员的 \(k[i] - 1\) 级上司可以安装与 \(y[i]\) 号成员的 \(k[i] - 1\) 级上司的直接通信线路,费用为 \(w[i]\)。(这 \(k[i]\) 条线路的费用独立计算)
如果一个集合内部的成员两两之间都可以通过直接或间接的通信线路进行通信,那么这个集合的所有成员可以成立一个特别行动组。
监察院想成立一个成员最多的特别行动组,同时他们想让安装线路的费用之和最小,所以他们找到了Q国的天命者——你,请你帮助他们规划出最优的线路。
输入
第一行为 \(2\) 个正整数 \(N\)、\(M\)。
第二行为 \(N - 1\) 个正整数 \(L[i]\),第 \(i\) 个正整数表示第 \(i+1\) 个成员的直接上司 \(L[i]\)。
接下来 \(M\) 行每行四个正整数 \(x[i]\),\(y[i]\),\(k[i]\),\(w[i]\)。
输出
仅一行,为特别行动组成员人数的最大值和在此前提下安装线路的最小费用之和。
输入示例
5 3
1 1 2 2
5 4 3 10
1 3 1 5
2 4 2 3
输出示例
5 21
数据规模及约定
对于 \(100\texttt{%}\) 的数据:
\(1 \le N、M \le 252501\)
\(1 \le x[i],y[i],k[i] \le N\),\(1 \le L[i] \le i - 1\),保证 \(x[i]\)、\(y[i]\) 号成员均至少有 \(k[i]\) 个上司,\(1 \le w[i] \le 10^9\)。
题解
这题 idea 非常妙啊。
考虑链的情况,我们就是将一个区间中的所有点和另一个等长区间所有点一一对应地连边。我们维护一下 ST 表,显然这个 ST 表分 \(log\) 层,我们每层用并查集维护连通性。当一类边加入时(令这类边链接区间 \(Q_1\) 和区间 \(Q_2\)),我们用 ST 表将区间拆成 \(log\) 个长度为 \(2\) 的整数次幂的区间,设对于长度为 \(2^k\) 的区间,属于 \(Q_1\) 那个区间叫 \(B_1\),属于 \(Q_2\) 的区间为 \(B_2\)。如果 \(B_1\) 和 \(B_2\) 不在同一连通块中,那么就将它们的连通块合并,然后递归处理 \(B_1、B_2\) 的左右半边;如果在同一连通块中,就直接 \(return\)。
对于树的情况,我们可以直接维护树上倍增的结构,令 \(S_{i, j}\) 表示节点 \(i\) 向上走 \(2^j\) 个节点组成的集合,然后对于不同的 \(j\) 用并查集分别维护连通性,操作类似上面所说的做法。最后找答案时我们只关心 \(j = 0\) 那一层的信息。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)
const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
if(Head == Tail) {
int l = fread(buffer, 1, BufferSize, stdin);
Tail = (Head = buffer) + l;
}
return *Head++;
}
int read() {
int x = 0, f = 1; char c = Getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
return x * f;
}
#define maxn 252510
#define maxlog 18
#define maxnode 4545180
#define LL long long
int n, m, Fa[maxn][maxlog], Id[maxn][maxlog];
struct Que {
int x, y, k, w;
Que() {}
Que(int _1, int _2, int _3, int _4): x(_1), y(_2), k(_3), w(_4) {}
bool operator < (const Que& t) const { return w < t.w; }
} qs[maxn];
int cnt, fa[maxnode], siz[maxn];
LL sum[maxn];
int findset(int x) { return x == fa[x] ? x : fa[x] = findset(fa[x]); }
void combine(int a, int b, int k, int val) {
int u = findset(Id[a][k]), v = findset(Id[b][k]);
if(u == v) return ;
fa[v] = u;
if(!k){ siz[u] += siz[v]; sum[u] += sum[v] + val; return ; }
combine(a, b, k - 1, val);
combine(Fa[a][k-1], Fa[b][k-1], k - 1, val);
return ;
}
int main() {
n = read(); m = read();
rep(i, 2, n) {
Fa[i][0] = read();
rep(j, 1, maxlog - 1) Fa[i][j] = Fa[Fa[i][j-1]][j-1];
}
rep(i, 1, m) {
int x = read(), y = read(), k = read(), w = read();
qs[i] = Que(x, y, k, w);
}
sort(qs + 1, qs + m + 1);
rep(i, 1, n) siz[Id[i][0] = ++cnt] = 1;
rep(i, 1, n) rep(j, 1, maxlog - 1) Id[i][j] = ++cnt;
rep(i, 1, cnt) fa[i] = i;
rep(i, 1, m) {
int x = qs[i].x, y = qs[i].y;
dwn(j, maxlog - 1, 0) if(qs[i].k >> j & 1) {
combine(x, y, j, qs[i].w);
x = Fa[x][j]; y = Fa[y][j];
}
}
int ans1 = 0;
LL ans2 = (1ll << 60);
rep(i, 1, n) {
int u = findset(i);
if(ans1 < siz[u]) ans1 = siz[u], ans2 = sum[u];
else if(ans1 == siz[u] && ans2 > sum[u]) ans2 = sum[u];
}
printf("%d %lld\n", ans1, ans2);
return 0;
}