【HNOI 2018】排列
Problem
Description
给定 \(n\) 个整数 \(a_1, a_2, \ldots , a_n(0 \le a_i \le n)\),以及 \(n\) 个整数 \(w_1, w_2, …, w_n\)。称 \(a_1, a_2, \ldots , a_n\) 的一个排列 \(a_{p[1]}, a_{p[2]}, \ldots , a_{p[n]}\) 为 \(a_1, a_2, \ldots , a_n\) 的一个合法排列,当且仅当该排列满足:对于任意的 \(k\) 和任意的 \(j\),如果 \(j \le k\),那么 \(a_{p[j]}\) 不等于 \(p[k]\)。(换句话说就是:对于任意的 \(k\) 和任意的 \(j\),如果 \(p[k]\) 等于 \(a_{p[j]}\),那么 \(k<j\)。)
定义这个合法排列的权值为 \(w_{p[1]} + 2w_{p[2]} + \ldots + nw_{p[n]}\)。你需要求出在所有合法排列中的最大权值。如果不存在合法排列,输出 \(-1\)。
样例解释中给出了合法排列和非法排列的实例。
Input Format
第一行一个整数 \(n\)。
接下来一行 \(n\) 个整数,表示 \(a_1,a_2,\ldots , a_n\)。
接下来一行 \(n\) 个整数,表示 \(w_1,w_2,\ldots ,w_n\)。
Output Format
输出一个整数表示答案。
Sample
Input 1
3
0 1 1
5 7 3
Output 1
32
Input 2
3
2 3 1
1 2 3
Output 2
-1
Input 3
10
6 6 10 1 7 0 0 1 7 7
16 3 10 20 5 14 17 17 16 13
Output 3
809
Explanation
Explanation for Input 1
对于 \(a_1=0,a_2=1,a_3=1\),其排列有
- \(a_1=0,a_2=1,a_3=1\),是合法排列,排列的权值是 \(1*5+2*7+3*3=28\);
- \(a_2=1,a_1=0,a_3=1\),是非法排列,因为 \(a_{p[1]}\) 等于 \(p[2]\);
- \(a_1=0,a_3=1,a_2=1\),是合法排列,排列的权值是 \(1*5+2*3+3*7=32\);
- \(a_3=1,a_1=0,a_2=1\),是非法排列,因为 \(a_{p[1]}\) 等于 \(p[2]\);
- \(a_2=1,a_3=1,a_1=0\),是非法排列,因为 \(a_{p[1]}\) 等于 \(p[3]\);
- \(a_3=1,a_2=1,a_1=0\),是非法排列,因为 \(a_{p[1]}\) 等于 \(p[3]\)。
因此该题输出最大权值 \(32\)。
Explanation for Input 2
对于 \(a_1=2,a_2=3,a_3=1\),其排列有:
- \(a_1=2,a_2=3,a_3=1\),是非法排列,因为 \(a_{p[1]}\) 等于 \(p[2]\);
- \(a_2=3,a_1=2,a_3=1\),是非法排列,因为 \(a_{p[1]}\) 等于 \(p[3]\);
- \(a_1=2,a_3=1,a_2=3\),是非法排列,因为 \(a_{p[1]}\) 等于 \(p[3]\);
- \(a_3=1,a_1=2,a_2=3\),是非法排列,因为 \(a_{p[2]}\) 等于 \(p[3]\);
- \(a_2=3,a_3=1,a_1=2\),是非法排列,因为 \(a_{p[2]}\) 等于 \(p[3]\);
- \(a_3=1,a_2=3,a_1=2\),是非法排列,因为 \(a_{p[1]}\) 等于 \(p[3]\)。
因此该题没有合法排列。
Range
对于前 \(20\%\) 的数据,\(1 \le n \le 10\);
对于前 \(40\%\) 的数据,\(1 \le n \le 15\);
对于前 \(60\%\) 的数据,\(1 \le n \le 1000\);
对于前 \(80\%\) 的数据,\(1 \le n \le 100000\);
对于 \(100\%\) 的数据,\(1 \le n \le 500000\),\(0 \le a_i \le n (1 \le i \le n)\),\(1 \le w_i \le 10^9\) ,所有 \(w_i\) 的和不超过 \(1.5 \times 10^{13}\)。
Algorithm
并查集
Mentality
这一题的题面很神仙,略难懂。
简而言之,有一棵树,告诉你每个节点的父亲和权值,只有选择了父亲才能选择儿子,如果当前选到的节点是你选的第 \(i\) 个,那么它的贡献为 \(i×\)权值 。问最大的总贡献。必须选完整棵树。
那么数据里不能有环,否则输出 \(-1\) 。
除去无解的情况,我们可以开始想想怎么做了:
首先想到一个很显然的错误贪心,那就是用一个优先队列维护当前能选的所有点,优先选权值最小的,不过这很好卡,若 \(i\) 的权值为 \(1e9\) ,其他所有节点的权值为 \(1e9-1\) ,\(i\) 的子树权值都为 \(1\) 。那这个贪心就死掉了。
但是不打紧,我们可以考虑这样一些东西:对于一个节点 \(i\) ,如果它的权值是最小的,那么当我们选完了 \(fa[i]\) ,我们必定会选择 \(i\) ,也就是说,对于权值最小的 \(i\) ,它一定会在 \(fa[i]\) 之后挨着选择。
所以我们可以考虑将最后答案的选择序列分割为 \(n\) 块,利用这个贪心一块块按顺序合并。
但是,对于两块 \(size>1\) 的序列,我们如何合并它们呢?
假设我已经选到了第 \(i\) 位,之后要按顺序合并 \(a\) 与 \(b\) 两块序列。
设 \(W_i\) 为序列 \(i\) 内元素权值和,\(w_{i_j}\) 代表序列 \(i\) 内第 \(j\) 个元素的权值。
那么如果我们先选 \(a\) 再选 \(b\) ,贡献就会是这样的:
\]
而先选 \(b\) 的话,贡献如下:
\]
则先选 \(a\) 更优,当且仅当 \(size_a*sum_b>size_b*sum_a\) 。
那么贪心策略便彻底确定下来了,我们只需要按证明出的结论的优劣性贪心选择并合并即可。
过程如下:
- 初始化并查集
- 选择堆顶元素,并检查元素是否为所在集合的根
- 合并当前元素与父亲所在集合,并计算对答案的贡献
完毕。
Code
#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;
int n, cnt, Fa[500001], head[500001], nx[500001], to[500001];
int fa[500001], size[500001];
long long w[500001], ans;
bool vis[500001], flag;
struct node {
int x, size;
long long w;
bool operator<(const node b) const {
return w * b.size > b.w * size;
} //定义比较函数
};
priority_queue<node> q;
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void Push(int x) { q.push(node{x, size[x], w[x]}); }
void Merge(int f, int x) {
ans += 1ll * size[f] * w[x]; //计算贡献
w[f] += w[x];
size[f] += size[x];
} //合并集合
void dfs(int x) {
if (flag) return;
if (vis[x]) flag = 1;
vis[x] = 1, cnt++;
for (int i = head[x]; i; i = nx[i]) dfs(to[i]);
}
int main() {
freopen("4437.in", "r", stdin);
freopen("4437.out", "w", stdout);
cin >> n;
for (int i = 1; i <= n; i++) {
scanf("%d", &Fa[i]);
nx[i] = head[Fa[i]], to[i] = i;
head[Fa[i]] = i;
}
dfs(0);
if (cnt < n || flag) {
cout << "-1";
return 0;
} //判断不合法情况--环
for (int i = 1; i <= n; i++) {
scanf("%lld", &w[i]);
ans += w[i];
fa[i] = i, size[i] = 1;
Push(i);
} //初始化并查集与答案
int x, f;
while (!q.empty()) {
x = q.top().x;
q.pop();
if (x != find(x)) continue; //判断是否为并查集根部元素
x = find(x);
fa[x] = f = find(Fa[x]); //合并
Merge(f, x);
if (f) Push(f); //加入堆内
}
cout << ans;
}
【HNOI 2018】排列的更多相关文章
- [HNOI 2018]排列
Description 题库链接 给定 \(n\) 个整数 \(a_1, a_2, \dots, a_n, 0 \le ai \le n\) ,以及 \(n\) 个整数 \(w_1, w_2, \do ...
- 【LG4437】[HNOI/AHOI2018]排列
[LG4437][HNOI/AHOI2018]排列 题面 洛谷 题解 题面里这个毒瘤的东西我们转化一下: 对于\(\forall k,j\),若\(p_k=a_{p_j}\),则\(k<j\). ...
- HNOI 2018 简要题解
寻宝游戏 毒瘤题. 估计考试只会前30pts30pts30pts暴力然后果断走人. 正解是考虑到一个数&1\&1&1和∣0|0∣0都没有变化,&0\&0& ...
- [HNOI/AHOI2018]排列 贪心
题面 题解: 把题面的限制换成中文: 如果排在第k位的下标 = 排在第j位的值 ,那么k < j 换一个描述方式: 一个值为x的数要排在第x个数后面. 再换一个描述方式: \(fa[i] = a ...
- [HNOI/AHOI2018]排列
[Luogu4437] 如果\(a[i]=j\)则序列\(p[]\)中\(j\)必须排在\(i\)前面,如果\(j\)不在范围内则不管,求一个式子\(\sum_{i=1}^n iw_{p[i]}\)的 ...
- 洛谷 P4437 [HNOI/AHOI2018]排列(贪心+堆,思维题)
题面传送门 开始 WA ycx 的遗产(bushi 首先可以将题目转化为图论模型:\(\forall i\) 连边 \(a_i\to i\),然后求图的一个拓扑序 \(b_1,b_2,\dots b_ ...
- [HNOI 2018]道路
Description 题库链接 给出一棵含有 \(n\) 个叶子节点的二叉树,对于每个非叶子节点的节点,其与左儿子相连的边为公路,其与右儿子相连的边为铁路.对于每个节点,选择一条与其儿子相连的铁路或 ...
- [HNOI 2018]游戏
Description 题库链接 有 \(n\) 个房间排成一列,编号为 \(1,2,...,n\) ,相邻的房间之间都有一道门.其中 \(m\) 个门上锁,其余的门都能直接打开.现在已知每把锁的钥匙 ...
- 【HNOI 2018】毒瘤
Problem Description 从前有一名毒瘤. 毒瘤最近发现了量产毒瘤题的奥秘.考虑如下类型的数据结构题:给出一个数组,要求支持若干种奇奇怪怪的修改操作(例如给一个区间内的数同时加上 \(c ...
随机推荐
- mac下supervisor安装及简单配置
supervisor是一个用 Python 写的进程管理工具,可以很方便的用来启动.重启.关闭进程(守护进程).可以用他来管理自己的“服务程序”. 安装 首先安装Python,Mac系统好像自带. 执 ...
- ArcGIS Server缓存清理
ArcGIS 发布服务,如果数据源没有注册到服务器话,会将数据复制到服务器指定目录.当发布一些较大的影像服务时,这种数据拷贝相当耗时. 所以,可以将数据粗处目录注册到ArcGIS Server服务器 ...
- 剑指offer——python【第36题】两个链表的第一个公共结点
题目描述 输入两个链表,找出它们的第一个公共结点 思路 注意,这里的公告结点的意思是相同的点,不仅值相同,next也相同,那么同理公共结点后面的点也是不仅值相同,而且next也相同,这样的话,就可以把 ...
- 四则运算第三次 PSP
- js方法的积累
对字符串编码解码编码 encodeURIComponet(str); 解码 decodeURIComponet(str); 及时反应的方法,监听input值是否发生改变$('#username') ...
- jsignature 中文开发手册
2017年5月9日21:23:17,最近比较忙,没时间写博客,真的是越来越懒来了 github:https://github.com/brinley/jSignature http://www.unb ...
- F#周报2019年第2期
新闻 Rider上的拼写助手,对未使用引用的代码分析及更多F#相关特性的更新 .NET开源革命开始 ML.NET 0.9发布记录 测试驱动的集成开发环境 Giraffe在GitHub上超过了1000个 ...
- Codeforces 1072 - A/B/C/D - (Done)
链接:http://codeforces.com/contest/1072/ A - Golden Plate - [计算题] #include<bits/stdc++.h> using ...
- Golang数据类型总结及其转换
golang数据类型 基本类型:boolean,numeric,string类型的命名实例是预先声明的. 复合类型:array,struct,指针,function,interface,slice,m ...
- [daily] 比端口转发更高级的ssh device tunnel转发
没有什么能够阻挡,你对自由的向往. 场景: 我有一台设备Server100,在某一个f复杂的内网里,需要多次ssh跳转可以访问到.但是它不能直接访问internet. 我现在需要在我的ssh路径上,搭 ...