程序设计与实践 虚假的树状数组 考试D题/T30 题解

Description

一年一度的数据结构发明大赛开始了,小张作为数据结构爱好者积极参加了这次比赛。他将自己参与竞赛的数据结构命名为"虚假的树状数组"。有别于我们常说的树状数组,这个数据结构没有什么实际作用,但还是比较有意思的,因此他光荣的获得了此次大赛的鼓励奖。

"虚假的树状数组"是一个 n 个结点的有向树,每个结点都有一个权值,编号为 i 的结点权值为 a_i 。树上的每一个结点可以生成一个数组,生成的方式为:[编号为 x 的结点的生成数组]=[ a_x ]+[ x 结点编号最小的儿子结点的生成数组]+…+[ x 结点编号最大的儿子结点的生成数组]。式子中的加号为数组的拼接,例如[1,1,2,1]+[1,2,2,3]=[1,1,2,1,1,2,2,3]。

同时,有些结点拥有一个排序标记,拥有排序标记的结点的生成数组需要按照从小到大的顺序排序。

现在,请你求出有向树的根节点的生成数组。

思路

考试时的做法(不想看的直接空降下一个标题即可)

先简单说一下我考试时候的做法
首先,我全开的long long
但是30题切记别开long long必定裂开

#define maxn 200005
typedef long long ll;
ll val[maxn];
bool sortmark[maxn] = {false};
vector<ll>map[maxn];
vector<ll>store[maxn];
bool root[maxn] = {false};

val 用来存节点的权值
sortmark用来记录每个节点用不用排序
map是个邻接表 map[i][0]...map[i][map[i].size()]是节点i的子节点们
store[i]是存储i节点对应的数据的,就是对应题目那个pdf里蓝色的字体部分。
root用来记录哪个是根节点,读取每一组数据的时候直接root[后一个节点] = true最后遍历root数组 哪个节点的值是false那哪个节点就是根节点了。

然后考试的时候我就写了一个dfs函数
跟题解里那个基本一样,并且事实上在没增加数据之前还是可以过30的

void dfs(ll u)
{
	store[u].push_back(val[u]); 
     for(ll i = 0 ; i < map[u].size() ; i++) 
     { 
          ll v = map[u][i]; 
          dfs(v); 
          store[u].insert(store[u].end(),store[v].begin(),store[v].end()); 
      } 
      if(sortmark[u]==true) 
      { 
         sort(store[u].begin(),store[u].end()); 
      } 
 }

只不过我是用的insert剩下的基本一样。
主函数里就正常输入输出,记得把sort(map[i].begin(),map[i].end());map给排序就可以
建议各位在做30之前先去vjudge上试试D题代码,搞明白了再来改30。vjudge链接详见30题上方的“补题链接”。硬改=血压upup=哇啊啊啊啊,其实是我太菜了

面向30题编程(从前面空降过来的可以落地了)

首先,不能使用

vector<ll>store[maxn];

这个对应6/7/8的RE。
另外,要对排序进行优化,不是所有点都需要被排序。
如果一个节点需要被排序,那么他的所有的子节点就不用排序了(反正到他这里还得排)
这个是对应最后一个样例8的TLE.

首先我要感谢讨论区的dl和谜语人们 ,提供了宝贵的思路和血压
在此感谢wzq大佬的23题题解,顺着看就能看懂+写出来真 是让我HIGH到不行 不错,如果有个点赞功能我必刷爆。

在翻了一圈讨论区之后,我发现可能对我这个菜鸡最容易实现的方法就是去记录每个节点对应的下标,也就是说如果这个节点需要被排序,那么被涉及到的最后的输出数组(vector)下标都有哪些。
既然有这种想法,那么我们就不需要vector<ll>store[maxn];了。
直接砍掉一维即可,最后直接输出vector<ll>store;

#define maxn 200005
typedef int ll;
ll val[maxn];
int seqcnt = 0;
bool sortmark[maxn] = {false};
vector<ll>map[maxn];
vector<ll>store;
bool root[maxn] = {false};

可以看到 我把long long换成了int,然后新增加了一个变量seqcnt用来记录每个节点有可能会涉及到的store中要排序的元素的下标(其实就是dfs的访问顺序),这个之后会说。同时我把我的dfs函数也改成了int类型。其他跟原来一样。
假如有个节点需要排序,那么他应该如何操作store这个vector才能达到排序的效果.

这样改完之后,需要克服的问题只有两个:

  1. 假如有个节点需要被排序,那么应该如何操作store这个vector才能达到排序的效果。
  2. 如何做到“假如一个节点需要被排序,那么他所有的子节点都不用被排序”。

先说第二个吧,第二个比较好做,第二个其实就是遍历一遍这个树,然后如果父节点的sortmarktrue那么所有子节点的sortmark就置为false即可。
直接单写一个dfs去遍历这个树。主要是因为我太菜了不知道怎么在一个dfs里写完

void sortdfs(ll u, bool sortjudge)

判断的逻辑就是,如果sortjudge == true(其实就是父节点u或者父节点以上的节点的sortmark为true过)或者sortmark[u] == true那么在遍历子节点v的时候就全部置为false。
如果不是的话就该咋着咋着正常遍历即可

sortdfs(v,sortmark[v]);

然后各位做到这里的时候,如果不放心可以调试一下看看有没有啥问题,别之后发现有的排序了有的没排序,然后发现是sortdfs写错了就头大了。

然后是第一个问题,这里我传一张图方便理解:
在这里插入图片描述
可以看到,对于最后我们要输出的store这个vector,每个节点涉及到的对于store中元素的改动跟他们的dfs访问顺序有关。(总感觉这句话说的不是人话)我举个例子吧。
比如节点3:
他本身的访问顺序是1(我这里的访问顺序是从0开始的,为了方便和store的下标对应)他的子节点的最大访问顺序是3,所以假如节点3要排序,他其实只需要对store的第1位到第3位进行排序即可。

 sort(store.begin()+1,store.begin()+3+1);

这里第二个参数多加了个1是因为sort函数是排序到第二个参数的前一位,所以要加个1.
(当然题目里节点3不需要被排序,我就是举个例子)
再比如节点5:
他本身的访问顺序是5,他的子节点的最大的访问顺序是6,所以假如节点5需要被排序,那么它只需要改动store的第5位到第6位。

 sort(store.begin()+5,store.begin()+6+1);

那问题又来了,如何获取子节点的访问顺序呢,其实就是在每次遍历完子节点之后

return seqcnt;

具体的改动我就不贴代码了。。再贴都快全贴出来了
这里简单说一下思路
首先还是push
首先整一个leftright记录最小访问顺序和最大访问顺序。
最小访问顺序其实就是父节点的访问顺序(因为dfs总不会d到头上去8)
最大访问顺序也初始化成seqcnt
然后就是标准的dfs遍历,每次遍历seqcnt都要++,然后如果比right大就换掉right
然后 因为我们之前已经做过sortdfs了,那么我们现在拿到的sortmark数组是真的需要排序的数组
所以直接判断需不需要排序,如果需要直接:

sort(store.begin()+left,store.begin()+right+1);

即可
最后

return seqcnt;

到这里这题应该就完事了。
最后祝各位点进来的小可爱国庆快乐,中秋快乐。
希望各位早点结束 折磨 所有作业。
去肝29了…

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值