[HNOI 2018]排列
2018-04-22 15:22
239 查看
Description
题库链接给定 \(n\) 个整数 \(a_1, a_2, \dots, a_n, 0 \le a_i \le n\) ,以及 \(n\) 个整数 \(w_1, w_2, \dots, w_n\) 。称 \(a_1, a_2, \dots, a_n\) 的 一个排列 \(a_{p[1]}, a_{p[2]}, \dots, a_{p
}\) 为 \(a_1, a_2, \dots, a_n\) 的一个合法排列,当且仅当该排列满足:对于任意的 \(k\) 和任意的 \(j\) ,如果 \(p[k]\) 等于 \(a_{p[j]}\) ,那么 \(k<j\) 。定义这个合法排列的权值为 \(w_{p[1]} + 2w_{p[2]} + \dots + nw_{p
}\) 。
求出在所有合法排列中的最大权值。如果不存在合法排列,输出 \(-1\) 。
\(1\leq n\leq 500000,0\leq a_i\leq n,1\leq w_i\leq 10^9\) ,\(\sum w_i\leq 1.5\times 10^{13}\)
Solution
假如我们对于所有的 \(i\) , \(a[i]\) 和 \(i\) 间建一条边,显然这副图可能构成了一棵树。如果不存在合法排列,当前仅当构成的图非树。
如何构成了树,那么原题的模型就变成了:给出一棵以 \(0\) 为根的有根树,需要为非 \(0\) 顶点标号 \(1\sim n\) ,并且满足父亲比自己先标号。每个节点有点权,树的价值为点权乘标号的和。求树最大的价值。
一个显然的贪心是如果当前树中权值最小的点 \(u\) 没有父亲,那么我们当前一定是选 \(u\) 。
不过大部分不是这种情况。
考虑如果 \(u\) 有父亲,显然当他的父亲被选之后马上就会选 \(u\) ,也就是说父子间的编号一定是相邻的。我们可以将 \(u\) 的答案并在他的父亲中。
同样的,对于两个不同的“块”,也是如此。
考虑一个长度为 \(l_1\) 的序列 \(A\) 和一个长度为 \(l_2\) 的序列 \(B\) ,
序列前面已经安排好了 \(loc\) 个。考虑 \(AB\) 和 \(BA\) 两种合并后的序列的答案:
\[W_{AB}=\sum_{i=1}^{l_1}(i+loc)w_{A_i}+\sum_{i=1}^{l_2}(i+loc+l_1)w_{B_i}\]
\[W_{BA}=\sum_{i=1}^{l_2}(i+loc)w_{B_i}+\sum_{i=1}^{l_1}(i+loc+l_2)w_{A_i}\]
如果 \(W_{AB}> W_{BA}\Rightarrow \frac{\sum_{i=1}^{l_1}w_{A_i}}{l_1}<\frac{\sum_{i=1}^{l_2}w_{B_i}}{l_2}\)
也就是平均权值小的放前面答案会更优。
那么我们就可以用堆来维护这个东西。
不知道为什么写了个支持删除的堆只有 50 ,然而不删去而在取出堆顶时判断是否合法就对了...
Code
#include <bits/stdc++.h> #define ll long long using namespace std; const int N = 500000+5; int n, a , fa , sz ; ll w ; struct node { int id; ll son, mom; node (int _id = 0, ll _son = 0, ll _mom = 0) {id = _id, son = _son; mom = _mom; } bool operator < (const node &b) const {return son*b.mom > b.son*mom; } }; priority_queue<node>Q; int find(int o) {return ~fa[o] ? fa[o] = find(fa[o]) : o; } void work() { memset(fa, -1, sizeof(fa)); scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); if (find(a[i])^find(i)) fa[find(a[i])] = find(i); else {puts("-1"); return; } } long long ans = 0; int loc = 0; for (int i = 1; i <= n; i++) { scanf("%lld", &w[i]); Q.push(node(i, w[i], 1)); sz[i] = 1; ans += w[i]; } memset(fa, -1, sizeof(fa)); while (!Q.empty()) { node t = Q.top(); Q.pop(); if (sz[t.id] != t.mom) continue; if (find(a[t.id]) == 0) { ans += w[t.id]*loc; fa[t.id] = 0; loc += sz[t.id]; }else { int tmp = find(a[t.id]); ans += w[t.id]*sz[tmp], fa[t.id] = tmp; w[tmp] += w[t.id], sz[tmp] += sz[t.id]; Q.push(node(tmp, w[tmp], sz[tmp])); } } printf("%lld\n", ans); } int main() {work(); return 0; }
相关文章推荐
- bzoj 5289: [Hnoi2018]排列
- [BZOJ5289][HNOI2018]排列(拓扑排序+pb_ds)
- [HNOI 2018]毒瘤
- [HNOI2018] 道路
- Fake or True(HNOI2018)
- [HNOI 2018]寻宝游戏
- 洛谷4438 [Hnoi2018]道路 【树形dp】
- BZOJ.1005.[HNOI2008]明明的烦恼(Prufer 高精 排列组合)
- 2018_蓝桥_next_permutation()函数_全排列函数总结
- [HNOI2018]道路(DP)
- [bzoj 1008] [HNOI2008]越狱:排列组合,快速幂
- 计蒜客 2018 蓝桥杯省赛 B 组模拟赛(一)-E. 代码填空:全排列
- bzoj 5290: [Hnoi2018]道路
- 排列 [HNOI2011]卡农
- [BZOJ1211][HNOI2004][prufer序列][排列]树的计数
- bzoj 5288: [Hnoi2018]游戏
- [HNOI 2018]转盘
- [BZOJ5285][HNOI2018]寻宝游戏
- 【排列组合】HNOI
- BZOJ 1008 [HNOI2008]越狱 排列组合