这个问题在接受采访时向我询问:
包含相邻值的不同分类子序列定义为长度为1 ,或者仅在排序时包含相邻数字。每个元素只能属于1个这样的子序列。我有Q查询,每个都更新A中的单个值,我必须回答每个查询,如果零件数量最小化,A分区中有多少部分进入不同的排序子序列。
例如,A = [1,2,3,4,3,5]的零件数量可以通过以下两种方式进行分割来最小化,两种方式都只包含两部分:
1) [1,2,3] && [4,3,5] ==> answer 2 (4,3,5 sorted is 3,4,5 all adjacent values)
2) [1,2,3,4,5] && [3] ==> answer 2
方法我尝试过:哈希和组合集但由于超时而未清除所有测试用例。
问题规则PDF: PDF
约束:
1<= N,Q <= 3*10^5
1< A(i)< 10^9
答案 0 :(得分:1)
在第一次传递时保留一个群集列表。每个都有一组值,具有最小值和最大值。这些集群可以很好地存储在段树中(以便在它们触摸时很容易合并)。
循环显示N个数字,并为每个数字添加到现有群集(可能触发合并),或创建新群集。如果您的群集存储min-1和max + 1,这可能会更容易。
现在你完成了N个数字的初始输入,并且你有几个集群,所有集群都可能具有合理的基数排序。
但是,您不想完成基数排序。生成计数列表,然后使用它来计算相邻的簇。循环遍历这一点,每次计数减少时,您都发现(差异)许多最终不同的排序子序列。 (使用max + 1再次付清,因为最后保证的零意味着您不必在循环后添加特殊情况。)
答案 1 :(得分:1)
首先,你可以在所有查询之前预处理A
并生成一个表(比如times_of
),这样当给出一个数字n
时,可以有效地获得次数{{1}通过像n
这样的表达式出现在A
中。在以下示例中,假设times_of[n]
的类型为A
,我们使用int[N]
来实现该表。其建设成本为O(NlogN)时间。
std::map
让auto preprocess(int *begin, int *end)
{
std::map<int, std::size_t> times_of;
while (begin != end)
{
++times_of[*begin];
++begin;
}
return times_of;
}
和min
分别成为max
的最小和最大元素。然后适用以下引理:
不同排序子序列的最小数量等于max {0,
A
-times_of[min]
} + ... + max {0,times_of[min-1]
-times_of[max]
}。
严格的证据有点技术性,所以我从这个答案中省略了它。粗略地说,考虑从小到大的数字。如果times_of[max-1]
显示超过n
,则必须带来额外的n-1
个子序列。
通过这个引理,我们可以在O(N)时间内初始计算不同的排序子序列times_of[n]-times_of[n-1]
的最小数量(通过迭代result
,而不是从times_of
迭代到{ {1}})。以下是示例代码:
min
要处理查询max
,我们首先更新费用为O(logN)时间的std::size_t result = 0;
auto prev = std::make_pair(min - 1, static_cast<std::size_t>(0));
for (auto &cur : times_of)
{
// times_of[cur.first-1] == 0
if (cur.first != prev.first + 1) result += cur.second;
// times_of[cur.first-1] == times_of[prev.first]
else if (cur.second > prev.second) result += cur.second - prev.second;
prev = cur;
}
和A[u] = v
。然后根据引理,我们只需重新计算常数(即4)相关项以更新times_of[A[u]]
。每次重新计算都花费O(logN)时间(以查找times_of[v]
中的上一个或下一个元素),因此查询总共需要O(logN)时间。