1.模拟实现这一树上二分的方法,最终TLE60分
#include <bits/stdc++.h>
using namespace std;
const int N = 2010;
typedef long long ll;
typedef struct node{
int num;//自己的编号
ll w;//自己的权值
ll w_all;//自己加上子树的权值
ll w_cost;// 删除该节点后,父节点需要增加的权值
bool is_live;// 节点是否存活的标志
node* fa;//指向自己的父亲
vector<node*> childs;//指向自己的孩子
//初始置零
node(){
num = w = w_all = 0;
fa = NULL;
}
}node;
node v[N]; //存放节点
//对于当前节点的每一个子节点c,函数会递归地调用自身,计算子节点c的累积权值
// 最后,将子节点c的累积权值加到当前节点的累积权值w_all上
// 这样,当函数遍历完整棵树后
// 每个节点的w_all都会包含它自己的权值以及所有子节点的累积权值。
void travel(node* now)
{
now -> w_all += now -> w;//首先将自己的权值w加到它的累积权值w_all上
for(node* c : now -> childs)
{
travel(c);
now->w_all += c->w_all;
}
}
signed main()
{
int n,m;//分别表示全部类别的数量和需要测试的类别的数量
cin>>n>>m;
for(int i = 1 ; i <= n ; i++)
{
v[i].num = i;
cin>>v[i].w;
}
for(int i = 2 ; i <= n ; i ++)
{
int f;//上级编号
cin>>f;
v[i].fa = &v[f];//&v[f]表示获取向量v的第f个元素的地址(它爹的地址)
v[f].childs.push_back(&v[i]);//指向儿子
}
node* root = &v[1];//其中编号为 1 的是根类别
travel(root);
while(m--)
{
int x;
cin>>x;
node* now = root;//一开始树是完整的
for(int i = 1 ; i <= n ; i++)
{
v[i].w_cost=0;
v[i].is_live=true;
}
//出口在哪?
while(1)
{
//需要找到这树上最小的点
node* ans = now;//先标记一个最大的root权值
queue<node*> Q;
Q.push(now);
bool cnt = false;
while(!Q.empty())
{
node* p = Q.front();
Q.pop();
/*
大模拟,模拟了一个树上二分的算法。
题目的公式是 其他的节点之和 - 自子子树的和
等价于 全部节点之和(根的子树之和) - 2倍自己子树之和
由于要删点,专门开一个cost去记录删去的值
真实的节点和是all-cost
综上:公式是abs((根的all-cost) - 2*(自己的all-cost))
这样就可以得到每一次的最小节点
然后去和每次给出的类别编号判断
然后选择去 保留 或者 删除
*/
//计算一下
ll A = abs(now->w_all-now->w_cost-2*(ans->w_all-ans->w_cost));
ll B = abs(now->w_all-now->w_cost-2*(p->w_all-p->w_cost));if (A > B || (A==B&&p->num < ans->num))
ans = p;
for (node* c : p->childs){
if (c->is_live){
cnt = true;
Q.push(c);
}
}
}
if (!cnt) break;//树上没有东西了
// 输出当前找到的最小节点
cout << ans->num << " ";
// 判断查询的节点是否在当前树上
node* p = &v[x]; // 将查询节点指针指向对应节点
bool flag = 0; // 标记查询节点是否在树上
while (p != now->fa)
{ // 遍历直到到达当前节点的父节点
if (p == ans)
{
flag = 1;
break;
}
// 如果查询节点是当前最小节点,标记为在树上
p = p->fa; // 向上移动到父节点
}
if (flag) { // 如果查询节点在树上
now = ans; // 保留当前最小节点
} else {
// 删除当前最小节点
ans->is_live = false; // 标记为已删除
node* p2 = ans->fa; // 获取当前最小节点的父节点
// 更新父节点的权值
while (p2 != now->fa) {
p2->w_cost += ans->w_all - ans->w_cost; // 更新父节点的删除权值
p2 = p2->fa; // 向上移动到父节点
}
}
}
cout << endl; // 输出换行符,表示当前查询结束
}
}