杂谈
杂谈
由于一些题目发不出来,所以这里放一些能发出来的题目
没错大部分题目还是口胡的小朋友们不要学哦
题目一 带权并查集
虽然这个知识点非常的简单,但是蒟蒻没有写过,所以蒟蒻就来写一写。
P1196 银河英雄传说
题目描述:
自己看,懒得打了(逃)
题解:
题目大家都会,这里强调几个代码细节:
- 并查集的实现:
int finf(int x){
if(fa[x] == x) return x;
int k = fa[x]; fa[x] = finf(fa[x]);
dis[now] += dis[k];
return fa[x];
}
这里注意我们先存档了原来的父亲,然后统计dis的时候,要加上原来的父亲的dis。这里非常容易想错。而且小样例不易错大样例de不出
2.merge的实现
为了实现merge,我们需要额外维护一个集合
i = finf(i); j = finf(j);
if(i != j){
fa[i] = j; dis[i] = num[j] + dis[i];
num[j] += num[i]; num[i] = num[j];
}
题目二 P4145 上帝造题的七分钟2 / 花神游历各国
题目描述
当然也没有
题解
我们考虑这个操作非常的线段树,但是由于上次的惨痛经验我们完全不用尝试直接维护,这种信息是无法通过常规手段维护的。
我们考虑这个操作其实非常浪费,因为一个数最多被操作
其实也非常简单,我们在线段树上每个节点维护一下这个节点是不是归零(准确的,归一)了,然后打上tag不下传即可。
题目三 P5889 跳树
题目描述
就是不写~
题解
蒟蒻不会做,但是蒟蒻会说:
一眼线段树,离线不下来所以我们考虑merge操作怎么办
相当于把左区间的点当做右区间操作的根节点,那么我们考虑会有哪几种情况:
- 如果右区间是往下走,那么答案就是左区间乘以右区间
- 如果右区间是往上走,那么情况稍显复杂。因为一个区间的相对位置是固定的,所以不好统计。
综上,我们考虑如何记录这个相对位置。
- 如果向下走,我们记录一个01串表示向下走到什么位置,合并时把两个字符串首尾相接即可
- 否则我们记录现在到了其第几层父亲即可
具体实现上,我们考虑一个二叉树上两个相对位置如何唯一确定。
- LCA(相对位置,比如说是这个节点的第几个父亲)
- 从这个父亲往下走的01串
那么合并是简单的。然后蒟蒻口胡完了。
TIPS
虽然说蒟蒻基本上把题目口胡了七七八八,但是具体实现上有一些细节口胡的时候没有考虑到,在这种情况下下笔是非常危险的,所以也不算口胡成功了把。
总结一下这种题你要保证细节不出问题,你要考虑以下几个东西:
- 具体合并方式及其(意义上的)正确性
- 合并复杂度
- 线段树节点的具体意义
- 线段树节点的信息维护(尽可能简单,但是必须全面)
P.S. 如果没有修改,那么这道题大概率可以不用考虑线段树的合并了(直接线段树往往不行),可以考虑离线下来枚举右端点然后线段树维护区间答案等等套路维护一些不好合并的区间查询。
另:像上面花神游历各国,如果操作变成平方(线段树大爹+1)并且支持离线,那么可以考虑使用莫队/分块的离线算法维护。
ABC376F cycle(hard)
这道题蒟蒻考场上面想了好久把所有需要的trick都找到了但是最后还是没写出来然后对着3000的数量级打了一个
好了前言到此结束我们来看题目。
首先有一个非常显然的暴力,我们令
不难发现上一步操作固定了一个位置,那么我们考虑去掉一维,然后用一个数组
但是这个时候复杂度仍然是
由于我们现在知道我们现在的两只手在哪里,目标的一只手在哪里,那么此时我们能转移到的状态只能是逆时钟转到位置和顺时钟转到位置的两种终局状态。那么这个时候问题就解决了。细节比较多,根据实现方式,码量有不少差距。
ABC376G 宝藏猎人
首先这是一道非常显然的树上贪心。我们考虑分几步来求解:
- 去掉限制,考虑怎么做
如果不在树上,那么我们直接从大往小排序,然后贪心地选择。
- 列出式子
假设我们现在已经有了一个操作序列,那么我们考虑如何计算这个序列的价值。
那么有
其实
- 考虑如何贪心
我们考虑贪心地选择操作。
在一个父节点,我们想要考虑哪一个儿子我们先搜完,那么我们考虑交换这两个儿子,然后通过上面的式子计算那个答案更小即可。在计算父亲答案的时候,我们可以假定儿子的答案已经求出。
在此基础上,我们考虑把贡献合并到父亲即可。由于我们不一定按照儿子父亲的顺序合并,所以我们需要维护一个并查集。
然后我们就愉快AC了。