P7339 Kotori-分治与排序
suzune_max · · 题解
题目链接
P7339 跳转
题意简介
给定
2^k 个数,每次两两分组比较大小,更大的数晋级下一轮。给定一个整数
m ,对于每次比大小都可以给任意双方加上m 从而改变可能的结果。需要判断第一个元素是否有可能是最终胜者。
题目分析 O(nlog^2n)
首先考虑贪心,但是贪心思路并不明显。
注意到比大小的结构类似淘汰赛,是一颗满二叉树,因此每个子树都是和原树相同的结构和问题,满足最优子结构。
所以考虑分治,分,解,合。
具体地,对于每个子问题,需要求所有可能获胜元素的集合。
那么对于分出的最小单位即一个元素,这个集合包含且只包含它自己一个元素,直接返回,这就是递归边界。
确定边界后,考虑普遍情况。对于某一非叶子节点,左子树有一可能获胜元素的集合,右子树也有一可能获胜元素的集合。
找出集合中的最小元素,与对手集合中的每一个元素作差,如果大于
即对于任意一个该层胜者元素
对于如何快速地找出最小值并找到可以获胜的元素,比较容易想到进行排序找出最小值后,使用二分查找找到对手集合第一个大于这个最小值的元素,此时该元素以右的所有元素均可以获胜。
再将这些获胜的元素放入一个集合中返回,等待下一层对战,同时维护集合单调保证二分可用。
最终只需判定首元素是否存在于根节点可行夺冠集合中即可。
:::success[关键代码]
vector<int> MergeSort(int l,int r,int dpt){
vector<int> sel,ser,se;//可能赢的人
se.push_back(a[l]);
if(l == r) return se;
se.erase(se.begin());
int mid = (l+r) >> 1;
//分
sel = MergeSort(l,mid,dpt+1);
ser = MergeSort(mid+1,r,dpt+1);
//合
//合并左集合和右集合,把一定输的踢出去
//左边有可能赢的
auto p = lower_bound(ser.begin(), ser.end(), sel[0]-m);
for(auto i=p;i!=ser.end();i++){
se.push_back(*i);
}
p = lower_bound(sel.begin(), sel.end(), ser[0]-m);
for(auto i=p;i!=sel.end();i++){
se.push_back(*i);
}
sort(se.begin(), se.end());
return se;//返回有序vct
}
:::
此外,对于在有序容器中判断某数是否存在,可以使用以下函数:
bool search(int x){
auto it = lower_bound(se.begin(), se.end(), x);//保证大于等于x
return it!=se.end() && !(x < *it);//判断是否没有大于等于x的数以及这个数是否小于x
}