题解 P5462 【X龙珠】
encore
2019-07-15 16:24:29
看了下大佬,似乎都是$\Theta(n)$链表过的?
没有办法我太弱了,考场上并没有想到这个方法。当时看到一个$n \leq 1e5$,一个最大值,就想用平衡树了。
~~然而这里并没有平衡树~~
好吧因为我需要两棵结构体平衡树,嫌麻烦就敲了两个set~~STL大法好~~
然后常数爆炸,慢的要死。。。
诶,思路?纯模拟吧。。。
贪心策略不用我讲了吧。。。
建两个set,一个把所有元素按数组下标排序,设为S2,一个把所有元素按数值大小排序,设为S1。
然后每次从S1里取出最大值(区间最值),再通过S2找出它的后一个元素(后驱),先后插入到队列里,再删除,就OK了吧。
~~不会set取最大值?最大值是set的最后一个元素,所以只要让一个iterator等于end再减减就好了~~
但是康到这里你可能觉得笔者太Naive了。比如这组数据:n = 4, 数组为 1, 2, 3, 4;
那么第一次取出来的值就是4, 而4没有后驱,这个时候就会出错。
~~辣鸡作者写的什么玩意儿~~
只要你继续看下去,这个问题会在两分钟内消失。考虑到每次选取(一对数中靠前的那个),只要不是当前序列的最后一个,这次选取就是合法的。
那么我们就有这样的解决方案:每次不把序列的最后一个放到set(平衡树)里,并加入特判。具体来说,对于以下数据:
n = 4, arr\[] = {1, 2, 3, 4};
最开始的时候,我们把除了最后一个元素都放进set, 此时set中的元素(为了方便理解只管一个set)为 {1, 2, 3}
再设个变量t2保存最后元素“4”和值(4)
第一次操作显然取出3。
然后特判:3是不是**set里的**最后一个元素? 是的。
我们把3 push到队列里, 然后找到3的前驱(下标排序), 是2。
然后把t2 push到队列里,做完这之后,让t2 = 2,也就是我们刚刚找出的前驱。
最后删除3,第一步操作就完成了。
如果n不变,arr\[] = {4, 3, 2, 1}
第一步取出4, 不是最后一个元素;
直接将4和3依次push到队列里,再把set里面的erase掉,这一步操作就完成了。
单次操作复杂度$\Theta(log_2 n)$, 总复杂度$\Theta(n log_2 n)$。真的慢的要死,还没sort的快。
好了,思路已经讲完了。其实这题如果手敲平衡树的话,对代码实现能力还是有很大帮助的。~~所以你们闲着无聊可以试试~~
具体看~~几乎不可读的~~代码吧
```cpp
#include <cstdio>
#include <iostream>
#include <map>
#include <set>
#include <queue>
constexpr int INF = 0x3f3f3f3f;
std::queue<int> q;
struct Node1{
int index, val;
Node1(void) : index(0), val(0) {}
Node1(int a, int b) : index(a), val(b) {}
bool operator<(const Node1 &a) const{
return this->index < a.index;
}
};
struct Node2{
int index, val;
Node2(void) : index(0), val(0) {}
Node2(int a, int b) : index(a), val(b) {}
bool operator<(const Node2 &a) const{
return this->val < a.val;
}
};
std::set<Node1> tts;\\原谅我不羁放纵乱取名
std::set<Node2> sst;\\上面的是按下标排,下面的是按数值排
int n;
int main(int argc, char const *argv[]) {
int *tmpArray;
scanf("%d", &n);
tmpArray = new int[n];
for (int i = 0; i != n; ++i)
scanf("%d", &tmpArray[i]);
for (int i = 0; i != n - 1; ++i)
sst.insert(Node2(i, tmpArray[i])),
tts.insert(Node1(i, tmpArray[i]));
std::set<Node2>::iterator i;
std::set<Node1>::iterator j;
int endindex, endval;
endindex = n - 1, endval = tmpArray[n - 1];
while (!sst.empty()) {
i = sst.end(); --i;\\取最值
int index1 = i->index, val1 = i->val;
j = tts.find(Node1(index1, val1));
int index2, val2;
++j;
if (j == tts.end()) {//是最后一个元素
--j;\\取前驱
index2 = endindex, val2 = endval;
if (j != tts.begin()) --j;\\防RE
endindex = j->index, endval = j->val;
sst.erase(Node2(endindex, endval)), tts.erase(j);
} else {
index2 = j->index, val2 = j->val;
sst.erase(Node2(index2, val2)), tts.erase(Node1(index2, val2));
}
sst.erase(Node2(index1, val1)), tts.erase(Node1(index1, val1));
q.emplace(val1), q.emplace(val2);
}
while (!q.empty()) {
printf("%d ", q.front()); q.pop();
}
return 0;
}
```
代码里可能含有很多“多此一举”的东西。。。
辣鸡作者滚粗了,有问题评论或私信吧