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)

posted @ 2020-05-26 14:45  funforever  阅读(146)  评论(0编辑  收藏  举报