博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
爬山 启发式合并 / STL
阅读量:4982 次
发布时间:2019-06-12

本文共 2039 字,大约阅读时间需要 6 分钟。

题目

其实 Kano 曾经到过由乃山,当然这名字一看山主就是 Yuno 嘛。当年 Kano 看见了由乃山,内心突然涌出了一股杜甫会当凌绝顶,一览众山小的豪气,于是毅然决定登山。

但是 Kano 总是习惯性乱丢垃圾,增重环卫工人的负担,Yuno 并不想让 Kano 登山,于是她果断在山上设置了结界……

Yuno 为了方便登山者,在山上造了 N 个营地,编号从 0 开始。当结界发动时,每当第 i(>0)号营地内有人,那么他将被传送到第 Ai(<i)号营地,如此循环,所以显然最后只会被传送到第 0 号营地。

但 Kano 并不知晓结界的情况。他登山的方法是这样的:首先分身出一个编号为 Gi 的 Kano,然后将其用投石机抛掷到营地 Di。Kano 总共做了 M 次这样的登山操作,但每次抛出去的 Kano 都被传送回了营地 0,所以 Kano 只好放弃了。

但是 Kano 在思考一个问题,到底每个营地被多少只编号不同的 Kano 经过过?

输入格式

第一行两个整数 N,M,表示山的营地数和登山次数。

接下来 N−1 行,每行一个数,第 i 行为 Ai,表示营地 i 将会传向营地 Ai。

接下来 M 行,每行两个数 Di,Gi。

输出格式

共 N 行,每行表示营地 i 有多少不同编号的 Kano 曾经通过。

样例数据

Input

5 400114 13 12 24 2

Output

22112

样例解释

1 号 Kano 曾被抛到 3,4 两个营地,传送轨迹分别是 3−1−0, 4−1−0

2 号 Kano 曾被抛到 2,4 两个营地,传送轨迹分别是 2−0, 4−1−0

所以 0,1,4 号营地被两只 Kano 经过过,2,3 号营地被一只 Kano 经过过。

数据规模与约定

\(5≤N≤100000,10≤M≤100000,max(Gi)≤1000000000\)

时间限制:1s

空间限制:512MB

思路

首先来想一想本题的暴力解法.

很直观的思路是对每一个营地用数组(或\(vector\)/\(set\)/\(queue\)/线段树/平衡树)维护一个集合,存放到达过该点的\(Kano\)的编号.当第\(i\)号营地里的\(Kano\)被传送到第\(A_i\)号营地时,把维护的第\(i\)号营地的集合合并到第\(A_i\)号营地的集合里.最后,每个营地的集合去重后的大小,就是曾经经过了该营地的不同编号的\(Kano\)的数量.

这样就有两个问题:一是合并的顺序;二是怎么合并两个数据结构.

第一个问题很好回答:我们可以倒序从\(n\)\(1\)遍历序列,每次计算出该营地的答案,同时把该营地的数据合并到\(A_i\)营地.这是因为\(A_i<i\),也就是说,无论哪次合并,都是"从后面的某个营地合并到前面的某个营地".换句话说,一个营地的状态,只与它和它后面的某些营地有关.

第二个问题也很好回答,只需要按照启发式合并的思想,每次都将小的集合合并到大的集合,这样可以获得优秀的\(O(nlog_2^n)\)的时间复杂度.

在代码实现中我使用了\(STL-set\),以省去去重的环节.

代码

#include
#include
using namespace std;int n,A[100005],m,G,D,Ans[100005],ID[100005];set
x[100005];inline void Merge(int u,int v){ if(x[ID[u]].size()
::iterator it=x[ID[v]].begin();it!=x[ID[v]].end();it++) x[ID[u]].insert(*it);}int main(){ scanf("%d%d",&n,&m); for(int i=2;i<=n;i++){ scanf("%d",&A[i]); A[i]++; } for(int i=1;i<=n;i++)ID[i]=i; while(m--){ scanf("%d%d",&G,&D); x[ID[G+1]].insert(D); } for(int i=n;i;i--){ Ans[i]=x[ID[i]].size(); if(A[i])Merge(A[i],i); }//将 i 合并到 A[i] for(int i=1;i<=n;i++)printf("%d\n",Ans[i]); return 0;}

总结

如果把题给的条件看成\(<i,A_i>\)的有向边,那么可以建出一个\(DAG\)图,而上面提到的倒序从\(n\)\(1\)就是该图的一个拓扑序,因此我们可以倒着合并.

转载于:https://www.cnblogs.com/TaylorSwift13/p/11209495.html

你可能感兴趣的文章
16进制的简单运算
查看>>
速读《Javascript模式》(一)(简介、var的变量提升以及es6新规范的let)
查看>>
DM8168集成图像算法
查看>>
GDI编程小结
查看>>
nalply/crtmpserver
查看>>
jquery 遍历节点
查看>>
工具选择
查看>>
(转)C#实现RSA非对称加密解密
查看>>
迅为iTOP-4412开发板-Android4.4-固定MAC
查看>>
centos下,安装MySQL以及配置远程连接等
查看>>
获取硬盘和CPU的序列号
查看>>
Python全栈开发 day2 - 数据类型详解
查看>>
葡萄城报表的数据可视化分析
查看>>
(转)面向对象的三大基石(封装,继承和复合,多态)
查看>>
jquery $.ajax $.get $.post的区别?
查看>>
python中运行pip出现Fatal error in launcher错误
查看>>
2017北京国庆刷题Day7 afternoon
查看>>
bzoj千题计划108:bzoj1018: [SHOI2008]堵塞的交通traffic
查看>>
C++集成设计环境——Code::Blocks安装过程
查看>>
Maven小记
查看>>