题目回顾
树上搜索https://sim.csp.thusaac.com/contest/32/problem/2
C++满分题解
思路概述
接下来设计数据结构。存在对树的很多插入,剪枝操作,毋庸置疑用链表实现最为合适。 由于完成一次类别测试后,需要树结构的恢复,所以增加parent结点来快速完成剪枝后的拼接而不用从输入重塑树结构。
typedef struct treeNode
{
int index;
long long weight;
treeNode* child;
treeNode* lastChild;
treeNode* brother;
treeNode* prevBrother;
treeNode* parent;
int childID;
treeNode(long long weight, int index) {
this->weight = weight;
this->index = index;
this->parent = nullptr;
this->child = nullptr;
this->lastChild = nullptr;
this->brother = nullptr;
this->prevBrother = nullptr;
}
} tree;
treeNode* myTree[2010];
下面我们来实现程序框图里的抽象过程 。
根据输入构建树结构
首先通过第一、二行输入,初始化所有的结点的 index 和 weight,通过第三行输入来实现结点之间的连接,构建出一棵树。我们定义一个函数来实现两结点之间的链接,顺序遍历第三行输入就能把一整棵树连接出来。
void linkNodes(treeNode* parent, treeNode* child) {
child->parent = parent;
if (!parent->child) {
// 如果parent结点还没有孩子
parent->child = child;
parent->lastChild = child;
}
else
{
// 如果parent结点已经有了孩子
parent->lastChild->brother = child;
child->prevBrother = parent->lastChild;
parent->lastChild = child;
}
}
计算当前结点各子孙结点的![\omega_{ \theta}](https://latex.csdn.net/eq?%5Comega_%7B%20%5Ctheta%7D)
记任意结点i的根子树的权重和为Ω,根节点为结点0,则有:
所以计算可以简化为计算各个结点的根子树权重和。定义一个函数并重载两种输入:
计算node结点的根子树权重和:
long long calculateWeight(treeNode* node) { //计算node的根子树权重和
long long weight1 = node->weight;
node = node->child;
while (node)
{
weight1 += calculateWeight(node);
node = node->brother;
}
return weight1;
}
以node为根,遍历其子树,计算 ,并找到最小的
void calculateWeight(treeNode* node, long long ¤tMin, int &index, long long currentSum) {
treeNode* temp;
long long weight_theata = abs(currentSum - 2 * calculateWeight(node));
if (weight_theata < currentMin || (weight_theata == currentMin && node->index < index)) {
currentMin = weight_theata;
index = node->index;
}
temp = node->child;
while (temp) {
calculateWeight(temp, currentMin, index, currentSum);
temp = temp->brother;
}
}
判断测试类别是否在选取结点的根子树上
简单来说就是判断选择的这个结点(最小的)是不是测试节点的祖先结点。
bool judge(int selectID, int testID) {
bool flag = false;
if (selectID == testID)
return true;
treeNode* currentNode = myTree[selectID]->child;
while (currentNode && !flag) {
flag = judge(currentNode->index, testID);
currentNode = currentNode->brother;
}
return flag;
}
减除所选结点为根的树
void removeBrunch(treeNode* node) {
treeNode* parent = node->parent;
if (!node->brother) {
// 如果他没有右兄弟
parent->lastChild = node->prevBrother;
}
else {
node->brother->prevBrother = node->prevBrother;
}
if (!node->prevBrother) {
//如果他没有左兄弟
parent->child = node->brother;
}
else {
node->prevBrother->brother = node->brother;
}
node->brother = nullptr;
node->prevBrother = nullptr;
node->parent = nullptr;
removedNode.push_back(node);
// 保存用以恢复树结构
removedNodesParent.push_back(parent);
}
重建回最原始的树
void rebuild() {
for (int i = 0; i < removedNode.size(); i++) {
linkNodes(removedNodesParent[i], removedNode[i]);
}
removedNodesParent.clear();
removedNode.clear();
}
完整代码:
# include <iostream>
# include<vector>
# include<cmath>
#include <algorithm>
using namespace std;
int n, m;
inline long long read() {
long long x = 0;
int signe = 1;
char c = getchar();
while (c < '0' || c>'9') {
if (c == '-')
signe = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
return x * signe;
}
typedef struct treeNode
{
int index;
long long weight;
treeNode* child;
treeNode* lastChild;
treeNode* brother;
treeNode* prevBrother;
treeNode* parent;
int childID;
treeNode(long long weight, int index) {
this->weight = weight;
this->index = index;
this->parent = nullptr;
this->child = nullptr;
this->lastChild = nullptr;
this->brother = nullptr;
this->prevBrother = nullptr;
}
} tree;
treeNode* myTree[2010];
void linkNodes(treeNode* parent, treeNode* child) {
child->parent = parent;
if (!parent->child) {
// 如果parent结点还没有孩子
parent->child = child;
parent->lastChild = child;
}
else
{
// 如果parent结点已经有了孩子
parent->lastChild->brother = child;
child->prevBrother = parent->lastChild;
parent->lastChild = child;
}
}
long long calculateWeight(treeNode* node) {
long long weight1 = node->weight;
node = node->child;
while (node)
{
weight1 += calculateWeight(node);
node = node->brother;
}
return weight1;
}
void calculateWeight(treeNode* node, long long ¤tMin, int &index, long long currentSum) {
treeNode* temp;
long long weight_theata = abs(currentSum - 2 * calculateWeight(node));
if (weight_theata < currentMin || (weight_theata == currentMin && node->index < index)) {
currentMin = weight_theata;
index = node->index;
}
temp = node->child;
while (temp) {
calculateWeight(temp, currentMin, index, currentSum);
temp = temp->brother;
}
}
bool judge(int selectID, int testID) {
bool flag = false;
if (selectID == testID)
return true;
treeNode* currentNode = myTree[selectID]->child;
while (currentNode && !flag) {
flag = judge(currentNode->index, testID);
if (flag)
break;
currentNode = currentNode->brother;
}
return flag;
}
vector<treeNode*> removedNode;
vector<treeNode*> removedNodesParent;
void removeBrunch(treeNode* node) {
treeNode* parent = node->parent;
if (!node->brother) {
// 如果他没有右兄弟
parent->lastChild = node->prevBrother;
}
else {
node->brother->prevBrother = node->prevBrother;
}
if (!node->prevBrother) {
//如果他没有左兄弟
parent->child = node->brother;
}
else {
node->prevBrother->brother = node->brother;
}
node->brother = nullptr;
node->prevBrother = nullptr;
node->parent = nullptr;
removedNode.push_back(node);
removedNodesParent.push_back(parent);
}
void rebuild() {
for (int i = 0; i < removedNode.size(); i++) {
linkNodes(removedNodesParent[i], removedNode[i]);
}
removedNodesParent.clear();
removedNode.clear();
}
int main() {
n = read(), m = read();
// 初始化结点及其权重+编号
for (int i = 1; i <= n; i++)
myTree[i] = new treeNode(read(), i);
//链接结点
for (int i = 2; i <= n; i++)
linkNodes(myTree[read()], myTree[i]);
//开始测试
for (int i = 1; i <= m; i++) {
int testID = read();
treeNode* currentNode = myTree[1];
//结束条件:只剩余一个结点
while (currentNode->child) {
//计算各个结点的wθ,并找到最小的
long long sum = calculateWeight(currentNode);
long long min = sum;
int minID = 0;
calculateWeight(currentNode, min, minID, sum);
cout << minID << " ";
treeNode* selectesNode = myTree[minID];
//判断当前选择结点与测试结点的从属关系
if (judge(minID, testID)) {
//如果判断为yes,只保留当前树枝
currentNode = myTree[minID];
}
else//如果判断为no,减除当前树枝
{
removeBrunch(selectesNode);
}
}
// 恢复最初的树结构
rebuild();
cout << '\n';
}
}