# P5290 [十二省联考2019]春节十二响

## 骗分方法

### 方法二

1. 1号点有一个儿子——详见方法一。
2. 1号点有两个儿子——把对这两个儿子下的两条链弄成两个堆，每次取出两个堆的堆顶，取 $max$ 加入答案，当一个堆取尽后，把另一个堆里的所有元素加入答案，最后加入 $M_1$

## 暴力方法

### 方法五

$M$ 值从大到小贪心地选。每选择一个就再从大到小把能选的都选上，然后把选择的这个的 $M$ 值加入答案。

## 考场代码

#include <bits/stdc++.h>
#define ll long long
#define pii pair<int, int>
#define mp make_pair
using namespace std;
const int N = 2e5 + 6;
int n, a[N], f[N];
ll ans = 0;

inline int rd() {
int x = 0;
char ch = getchar();
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + (ch - '0'), ch = getchar();
return x;
}

inline bool pd1() {
for (int i = 2; i <= n; i++) if (f[i] != i - 1) return 0;
return 1;
}

inline void P101112_1() {
for (int i = 1; i <= n; i++) ans += a[i];
cout << ans << endl;
}

namespace P101112_2 {
int deg[N];

inline bool pd() {
for (int i = 2; i <= n; i++) ++deg[f[i]];
if (deg[1] != 2) return 0;
for (int i = 2; i <= n; i++) if (deg[i] > 1) return 0;
return 1;
}

int son[N];
priority_queue<int> q[2];

inline void work() {
int g[2], t = 0;
for (int i = 2; i <= n; i++)
if (f[i] == 1) g[t++] = i;
else son[f[i]] = i;
ans = a[1];
for (int i = 0; i < 2; i++) {
int x = g[i];
while (x) q[i].push(a[x]), x = son[x];
}
while (q[0].size() && q[1].size())
ans += max(q[0].top(), q[1].top()), q[0].pop(), q[1].pop();
for (int i = 0; i < 2; i++)
if (q[i].size())
while (q[i].size()) ans += q[i].top(), q[i].pop();
cout << ans << endl;
}
}

namespace TX {
const int M = 2e3 + 6;
bitset<M> b[M], o, v;
vector<int> e[M];
int st[M], top = 0, p[M];
pii g[M];

void dfs(int x) {
b[p[x]] = o;
for (int i = 1; i <= top; i++) b[p[st[i]]][p[x]] = 0;
st[++top] = x;
o[p[x]] = 0;
for (unsigned int i = 0; i < e[x].size(); i++) {
int y = e[x][i];
if (!o[p[y]]) continue;
dfs(y);
}
o[p[x]] = 1;
--top;
}

inline void work() {
for (int i = 2; i <= n; i++) e[f[i]].push_back(i);
for (int i = 1; i <= n; i++) g[i] = mp(a[i], i);
sort(g + 1, g + n + 1);
for (int i = 1; i <= n; i++) p[g[i].second] = i;
o.set();
dfs(1);
v.reset();
for (int i = n; i; i--) {
if (v[i]) continue;
o = b[i];
ans += g[i].first;
v[i] = 1;
for (int j = i - 1; j; j--)
if (o[j] && !v[j]) {
v[j] = 1;
o &= b[j];
}
}
cout << ans << endl;
}
}

int main() {
cin >> n;
for (int i = 1; i <= n; i++) a[i] = rd();
for (int i = 2; i <= n; i++) f[i] = rd();
if (pd1()) {
P101112_1();
return 0;
}
if (P101112_2::pd()) {
P101112_2::work();
return 0;
}
if (n < 2001) {
TX::work();
return 0;
}
return 0;
}

## 代码实现细节

ouuan 的博客十二省联考2019 游记 & 题解中，对此有这样的说法：

swap 在不开 C++11 的情况下是 $O(n)$ 的，开 C++11 则是 $O(1)$ 的，如果不开 C++11 可以记录 id 然后交换 id

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 6;
int n, a[N], f;
vector<int> e[N], o;
priority_queue<int> q[N];

inline void merge(int x, int y) {
if (q[x].size() < q[y].size()) swap(q[x], q[y]);
while (q[y].size()) {
o.push_back(max(q[x].top(), q[y].top()));
q[x].pop(), q[y].pop();
}
while (o.size()) q[x].push(o.back()), o.pop_back();
}

void dfs(int x) {
for (unsigned int i = 0; i < e[x].size(); i++)
dfs(e[x][i]), merge(x, e[x][i]);
q[x].push(a[x]);
}

int main() {
cin >> n;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 2; i <= n; i++) scanf("%d", &f), e[f].push_back(i);
dfs(1);
long long ans = 0;
while (q[1].size()) ans += q[1].top(), q[1].pop();
cout << ans << endl;
return 0;
}
posted @ 2019-04-11 04:49 xht37 阅读(...) 评论(...) 编辑 收藏