题面

题解:

把题面的限制换成中文:

如果排在第k位的下标 = 排在第j位的值 ,那么k < j

换一个描述方式:

一个值为x的数要排在第x个数后面。

再换一个描述方式:

\(fa[i] = a_i\)

仿佛有什么不对劲?

嗯这其实就是一棵树。

并且我们可以发现,这棵树一定以一个虚拟节点0为根,并且有合法排列,也就是树没有环,当且仅当从0开始遍历,可以遍历到所有节点。

且排列合法当且仅当我们在访问一个节点之前,先访问它的父亲,也就相当于在树上走。

因为如果不以0为根,那么由于题面中所说权值大小在\([1, n]\)的区间内,我们根本无法找到进入这样一些关系的"入口",因为不管我们尝试从那个点开始走,都必须要先去这个点的父亲,而要去这个点的父亲,又要先去这个点的父亲的父亲……因为在这棵树中,我们唯一可以直接到达的点是虚拟节点0,所以如果这些关系组成的边无法通向0的话,我们只能在无限个这样的限制中绕圈,永远无法找到入口,即没有合法的解。

因此我们真正的题面其实是:

给定一个n + 1个节点的树,依次取点,满足取儿子之前,必须要取它的父亲。每个点有权值,第i个取的点的权值会被乘上i,求一个合法方案,使得取完所有点后权值之和最大。

根据贪心的原则,一个点的权值越小,就越要优先取。

因此我们考虑整棵树中权值最小的那个节点。

1,如果这个节点没有父亲(或父亲已经被取走),那我们肯定先取它。

2,如果这个点有父亲,那么一旦我们取了它的父亲,我们肯定会马上就取它(此时满足情况1)

综上,我们得到一个结论,如果我们每次考虑权值最小的那个节点,这个节点要么没有父亲(f[i] = 0),要么一定会紧挨着它的父亲取。

所以这个点和它的父亲在最优排列中一定是相邻的,因此我们可以考虑合并这2个节点。

那么实际上,树中的每个节点就代表了一段排列,现在来考虑一段排列怎么评估一段排列的权值

假设有一个长度为\(m_1\)的序列\(a\),和一段长度为\(m_2\)的序列\(b\),我们表示出序列\(ab\)和序列\(ba\)的权值

\[W_{ab} = \sum_{j = 1}^{m_1}(i + j)W_{a_j} + \sum_{j = 1}^{m_2}(i + j + m_1)W_{b_j}
\]

\[W_{ab} = \sum_{j = 1}^{m_2}(i + j)W_{b_j} + \sum_{j = 1}^{m_1}(i + j + m_2)W_{a_j}
\]

如果排列\(ab\)优于排列\(ba\),那么\(W_{ab} > W_{ba}\),即先选\(a\)更优

那么有:

\[W_{ab} - W_{ba} = m_1W_b - m_2W_a > 0
\]

\[\frac{W_b}{m_2} > \frac{W_a}{m_1}
\]

因此我们只需要以平均权值作为新的权值来考虑即可。

根据上诉式子,可以得到,如果我们把序列\(b\)放在序列\(a\)后面,可以得到一个独立的\(m_1 \cdot W_b\)的贡献,因此在不断合并节点的过程中统计贡献即可

\(\Delta\)此题有点卡精度,请用long double……

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define R register int
  4. #define AC 501000
  5. #define LL long long
  6. #define ld long double
  7. int n, rnt;
  8. LL ans, sum[AC];
  9. int v[AC], fa[AC], cnt[AC];//cnt记录每个节点的版本号
  10. int Next[AC], last[AC], belong[AC], Size[AC];//用并查集+双向链表来维护已经合并的序列
  11. struct node{int id, x; ld w;};//当前点版本号+编号+值
  12. struct cmp{bool operator() (node a, node b){return a.w > b.w;}};
  13. struct road{
  14. int Head[AC], Next[AC], date[AC], tot;
  15. inline void add(int f, int w){
  16. date[++ tot] = w, Next[tot] = Head[f], Head[f] = tot;
  17. }//用父亲向儿子连边
  18. }E;
  19. node _s[AC]; int _top;//check
  20. struct STL_Delete_queue{
  21. priority_queue<node, vector<node>, cmp> q1;
  22. void del(int x){++ cnt[x];}//删除编号为x的并查集
  23. void push(node x){q1.push(x);}
  24. int top()
  25. {
  26. while(!q1.empty() && q1.top().id != cnt[q1.top().x]) q1.pop();
  27. int x = q1.top().x;
  28. q1.pop();
  29. return x;
  30. }
  31. void check()
  32. {
  33. _top = 0;
  34. while(!q1.empty()) _s[++ _top] = q1.top(), q1.pop();
  35. for(R i = 1; i <= n; i ++)
  36. printf("%d %.3Lf\n", _s[i].x, _s[i].w);
  37. for(R i = 1; i <= _top; i ++) q1.push(_s[i]);
  38. }
  39. }q;
  40. inline int read()
  41. {
  42. int x = 0;char c = getchar();
  43. while(c > '9' || c < '0') c = getchar();
  44. while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
  45. return x;
  46. }
  47. int find(int x)
  48. {
  49. if(belong[x] == x) return x;
  50. else return belong[x] = find(belong[x]);
  51. }
  52. void dfs1(int x)//先判断是否合法
  53. {
  54. ++ rnt;//和0相连的点必定形成一个合法的树,其他不合法的环都是独立出来的。
  55. for(R i = E.Head[x]; i != -1; i = E.Next[i])
  56. dfs1(E.date[i]);//所以只需要记录从0开始遍历,可以遍历到多少点即可
  57. }
  58. void pre()
  59. {
  60. n = read();
  61. for(R i = 0; i <= n; i ++) E.Head[i] = Next[i] = last[i] = -1;//因为有0号节点,因此先全都赋值为-1
  62. for(R i = 1; i <= n; i ++) fa[i] = read(), E.add(fa[i], i), belong[i] = i;
  63. for(R i = 1; i <= n; i ++)
  64. {
  65. v[i] = sum[i] = read(), ans += v[i];
  66. q.push((node){0, i, (ld)v[i]}), Size[i] = 1; //维护集合的信息
  67. }
  68. }
  69. void work()
  70. {
  71. for(R i = 1; i <= n; i ++)//因为一共有n + 1个节点,所以会合并n次
  72. {
  73. int x = q.top(), fx = find(fa[x]);
  74. belong[x] = fx, q.del(fx);
  75. ans += Size[fx] * sum[x], Size[fx] += Size[x], sum[fx] += sum[x];
  76. if(fx) q.push((node){cnt[fx], fx, (ld)sum[fx] / (ld)Size[fx]});
  77. }
  78. printf("%lld\n", ans);
  79. }
  80. int main()
  81. {
  82. // freopen("in.in", "r", stdin);
  83. pre();
  84. dfs1(0);
  85. if(rnt != n + 1) printf("-1\n");
  86. else work();
  87. // fclose(stdin);
  88. return 0;
  89. }

[HNOI/AHOI2018]排列 贪心的更多相关文章

  1. 【LG4437】[HNOI/AHOI2018]排列

    [LG4437][HNOI/AHOI2018]排列 题面 洛谷 题解 题面里这个毒瘤的东西我们转化一下: 对于\(\forall k,j\),若\(p_k=a_{p_j}\),则\(k<j\). ...

  2. 洛谷 P4437 [HNOI/AHOI2018]排列(贪心+堆,思维题)

    题面传送门 开始 WA ycx 的遗产(bushi 首先可以将题目转化为图论模型:\(\forall i\) 连边 \(a_i\to i\),然后求图的一个拓扑序 \(b_1,b_2,\dots b_ ...

  3. BZOJ5289 HNOI/AHOI2018排列(贪心+堆)

    题面描述的相当绕,其实就是如果ai=j,重排后ai要在aj之后.同时每个ai有附属属性wi,要求最大化重排后的Σiwi. 容易发现这事实上构成一张图,即由j向i连边.由于每个点入度为1或0,该图是基环 ...

  4. 【洛谷 P4437】 [HNOI/AHOI2018]排列(贪心,堆)

    题目链接 如果\(j<=k,a_{p[j]}!=p[k]\)可以理解为如果\(a_{p[j]}=p[k]\),那么\(k\)一定要放在\(j\)前面,也就是\(a_j\)在\(j\)前面. 于是 ...

  5. [HNOI/AHOI2018]排列

    [Luogu4437] 如果\(a[i]=j\)则序列\(p[]\)中\(j\)必须排在\(i\)前面,如果\(j\)不在范围内则不管,求一个式子\(\sum_{i=1}^n iw_{p[i]}\)的 ...

  6. BZOJ5289 & 洛谷4437:[HNOI/AHOI2018]排列——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=5289 https://www.luogu.org/problemnew/show/P4437 考虑 ...

  7. Poj2054 color a tree && [HNOI/AHOI2018]排列

    https://zybuluo.com/ysner/note/1120723 题面 原题 某省选强化题 大致意思是给你一颗树,选父亲后才能选儿子. 每个点对答案的贡献为你在第几次选这个点 × 该点权值 ...

  8. luogu P4437 [HNOI/AHOI2018]排列

    luogu 问题本质是把\(a_i\)作为\(i\)的父亲,然后如果有环就不合法,否则每次要取数,要满足取之前他的父亲都被取过(父亲为0可以直接取),求最大价值 贪心想法显然是要把权值大的尽量放在后面 ...

  9. 【题解】Luogu P4436 [HNOI/AHOI2018]游戏

    原题传送门 \(n^2\)过百万在HNOI/AHOI2018中真的成功了qwqwq 先将没门分格的地方连起来,枚举每一个块,看向左向右最多能走多远,最坏复杂度\(O(n^2)\),但出题人竟然没卡(建 ...

随机推荐

  1. CentOS7安装及配置vsftpd (FTP服务器)

    CentOS7安装及配置vsftpd (FTP服务器) 1.安装vsftpd 1 yum -y install vsftpd 2.设置开机启动 1 systemctl enable vsftpd 3. ...

  2. 基于ejabberd实现各个客户端消息同步

    先上图再说(左侧是web端,右侧是ios端)              要实现上面的功能,如果所有设备都在线的话,那么carboncopy(xmpp xep-0280协议)这个模块是可以实现接收到的消 ...

  3. 8个数据清洗Python代码,复制可用,最长11行 | 资源

    最近,大数据工程师Kin Lim Lee在Medium上发表了一篇文章,介绍了8个用于数据清洗的Python代码. 数据清洗,是进行数据分析和使用数据训练模型的必经之路,也是最耗费数据科学家/程序员精 ...

  4. Echarts简单图表

    一.实现要点 常用可视化图表库 Echarts HighCharts D3.js neo4j (NOSQL) 1.导入js库 <script type="text/javascript ...

  5. windows下sublime text的node.js开发环境搭建

    首先安装sublime text3,百度一堆,自己找吧.理论上sublime text2应该也可以.我只能说一句:这个软件实在是太强悍了. 跨平台,丰富的插件体系,加上插件基本上就是一个强悍的ide了 ...

  6. centos 7 install gnome etc

    centos yum 有grouplist子命令,可以查看当前系统有多少软件组件,里面就有gnome:"GNOME Desktop" sudo yum groupinstall G ...

  7. k8s zookeeper、kafka部署

    安装zookeeper apiVersion: v1 kind: ConfigMap metadata: name: zookeeper-config namespace: kube-system a ...

  8. UVA 11542 高斯消元

    从数组中选择几个数,要求他们的乘积可以开平方,问有多少种方案. 先将单个数拆分成质因子,对于这个数而言,那些指数为奇数的质因子会使这个数无法被开平方. 所以我们需要选择一个对应质因子指数为奇数的元素, ...

  9. socket编程 123

    1. 预备知识 一直以来很少看到有多少人使用php的socket模块来做一些事情,大概大家都把它定位在脚本语言的范畴内吧,但是其实php的socket模块可以做很多事情,包括做ftplist,http ...

  10. 2017软工 — 每周PSP

    1. PSP表格 2. PSP饼图 3. 本周进度条 4. 累计折线图