Linova and Kingdom CodeForces - 1336A
贪心好难啊,不好猜。
这道题的话,易得,如果一个节点是工业区的话,它的子节点也都是工业区,因为否则我们对调该节点和子节点,答案就多了1。所以说是先选子节点在选择父节点作为工业区,同理如果假定全为工业区,我们选择旅游区的话,我们应该先选择父节点为旅游区,所以我们选择策略时要避开这种情况,我们先不考虑这种情况,在得出的策略中判断是否会出现这种情况,如果不出现这种情况则说明这种策略可以用。
我们先假设全部都是工业区,那么快乐值为0,如果我们把某一个设成旅游区,那么快乐值会变成sub - deep,其中sub是包括本身和子节点的和,比如样例1中1的sub就是8,7个子节点包括自己就是8了,deep就是深度,我们假定root的deep为1,那么样例1的root就为1,这样如果把1变成旅游区快乐值就能加8-1=7(注意k是工业区的数量,旅游区的数量为n-k)。为什么是这样呢?众所周知,该节点如果变成了旅游区,那么它的子节点的快乐值都会加1,这就加了sub-1了,但是由于自己变成了旅游区,自己的快乐值就没了,减少了deep-1(因为根据第一段的策略,在选此节点作为旅游区时,其父节点必都为旅游区)。这样,实际上就多了sub - deep的快乐值。
同理,在修改完第一个之后找第二个变成旅游区,分三种情况:和第一个节点同深度,是第一个节点的子节点,是第一个节点的父节点(因为这种情况下没有先选父节点,所以舍去)。
如果和第一个节点同深度,显然又多了sub-deep的快乐值(因为第一个并不影响第二个)。
如果第二个是第一个节点的子节点,那么我们计算子节点快乐值时增加量是sub-1,快乐值减少的仍然是deep-1。
如果第二个是第一个节点的父节点,舍去这种情况。
综上,我们只要保证选择策略时凡是选择了子节点为旅游区,那么它父节点必为旅游区的情况下,对sub - deep排序即可,我们知道父节点的sub大于子节点的sub,父节点的deep小于子节点,所以父节点的sub-deep大于子节点的,我们对与sub-deep从大到小排序即可保证在选择了子节点为旅游区的情况下,它父节点必为旅游区。
AC代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; ll ans; vector<int> v[200005]; struct Node{ int sub; int deep; int diff; friend bool operator <(Node x, Node y){ return x.diff > y.diff; } }node[200005]; bool vis[200005]; int dfs(int index, int deeps) { node[index].deep = deeps; for (int i = 0; i < v[index].size(); ++i) { if(!vis[v[index][i]])//如果没被遍历过则说明该节点为index的子节点 { vis[v[index][i]] = true; node[index].sub += dfs(v[index][i], deeps + 1);//子节点数量 } } node[index].diff = ++node[index].sub - node[index].deep;//子节点数量加上自身变成了sub return node[index].sub; } int main() { int n, k; int a, b; scanf("%d %d", &n, &k); for(int i = 0; i < n - 1; i++) { scanf("%d %d", &a, &b);//树虽然有方向,但是题目不告诉你,但是可以从根节点开始拓展,一直拓展到叶节点即可 v[a].push_back(b); v[b].push_back(a); } vis[1] = true; dfs(1, 1); sort(node + 1, node + n + 1); for(int i = 1; i <= n - k; i++) { ans += node[i].diff; } printf("%lld\n", ans); return 0; }
PS:
template< class RandomIt, class Compare > void nth_element( RandomIt first, RandomIt nth, RandomIt last, Compare comp ); /* nth_element 是部分排序算法,它重排 [first, last) 中元素,使得: nth 所指向的元素被更改为假如 [first, last) 已排序则该位置会出现的元素。 这个新的 nth 元素前的所有元素小于或等于新的 nth 元素后的所有元素。 更正式而言, nth_element 以升序部分排序范围 [first, last) ,使得对于任何范围 [first, nth) 中的 i 和任何范围 [nth, last) 中的 j ,都满足条件 !(*j < *i) (对于 (1-2) ,对 (3-4) 则为 comp(*j, *i) == false )。置于 nth 位置的元素则准确地是假如完全排序范围则应出现于此位置的元素。 */
所以sort那一行可以换成:
nth_element(node + 1, node + n - k, node + n + 1);
复杂度从O(nlogn)变成了O(n)