树上搜索ccf csp第32次认证第三题

题目描述

树上搜索
ccf csp第32次认证第三题
原题链接:http://118.190.20.162/view.page?gpid=T178

样例

输入

5 2
10 50 10 10 20
1 1 3 3
5
3

输出

2 5
2 5 3 4

思路

本题是一道大模拟题,用到的算法不难但杂,稍不注意就会出错

解题关键

1.对于每一个类别,统计它和其全部后代类别的权重之和,同时统计其余全部类别的权重之和,并求二者差值的绝对值,选择绝对值最小的类别。这个条件直接做需要时刻维护每次迭代二者差值的绝对值,比较麻烦,于是转化为求每个节点及后代权重和的两倍与当前树总权重差的绝对值,当两者绝对值最小时就可以得到当前查询的节点。

2.不仅要记录当前节点的孩子节点,也到同时记录当前节点的父节点。前者用链表存,后者直接用数组存。

3.数据会爆int ,要开long long

题解

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 2010;

int idx,h[N],e[N],ne[N],w[N];
int fa[N];
int n,m;
LL u[N],uu[N];
bool st[N];
//st是表示每个节点的状态,st=true说明这个节点在树中,可以访问,st=false说明这个节点已经被删除,不能访问

void add(int a,int b){
    ne[idx]=h[a];
    e[idx]=b;
    h[a]=idx++;
}

LL dfs(int x){
    LL sum=w[x];
    for(int i=h[x];i!=-1;i=ne[i]){
        int j=e[i];
        uu[j]=dfs(j);
        sum+=uu[j];
    }
    return sum;
}


//返回当前节点最有可能被查询的那个节点
int query(int head,LL all){
    int temp=head;
    for(int i=h[head];i!=-1;i=ne[i]){
        int j=e[i];
        if(!st[j]) continue;
        int p=query(j,all); //找到它的子节点最有可能被查询的那个节点
        if(abs(u[p]*2-all)<abs(u[temp]*2-all)) temp=p;//差值更小,直接更新即可 
        else if(abs(u[p]*2-all)==abs(u[temp]*2-all)&&p<temp) temp=p;//差值一样,取编号更小的节点
    }
    return temp;
}


int main(){
    memset(h,-1,sizeof h);
    cin>>n>>m;
    for(int i=1;i<=n;i++) {
        cin>>w[i];
    }
    fa[1]=0;
    for(int i=2;i<=n;i++){
        int a;
        cin>>a;
        fa[i]=a;
        add(a,i);
    }
    uu[1]=dfs(1);
    while(m--){
        memset(st,true,sizeof st);
        memcpy(u,uu,sizeof uu);//每次迭代前利用uu重置u数组,典型的空间换时间
        int head=1;
        int x;
        cin>>x;//x是我们想要的那个节点
        while (1)
        {
            int t=query(head,u[head]);//t是现在要判断的节点 
		    bool fl=false;
			for(int i=h[head];i!=-1;i=ne[i]){
		        int j=e[i];
		        if(!st[j]) continue;
		        fl=true;
		    }
		    if(!fl) break;//当现在整个树只有一个节点时,退出循环
            cout<<t<<" ";
            bool flag=false;
            for(int i=x;i!=0;i=fa[i]){
                if(i==t) flag=true; //目标节点是否为当前节点的后代节点
            }
            if(flag){
                head=t;//如果是则头节点改成当前节点
            }
            else{
                st[t]=false;//如果不是那么一定在另一部分,头节点不变,但要把当前节点及其后代节
                //点从树中删除,只需要修改当前节点的状态即可,因为dfs查询的时候是从上往下查询
                //到,到不了父节点,自然也到不了它的子节点。
                for(int i=fa[t];i!=0;i=fa[i]){ 
                    u[i]-=u[t];//这个子树删除了,它的父节点的加权和也要修改
                }
            }
        }
        puts("");
    }
    return 0;
}

### CCF-CSP 第35认证第三目分析与解法 关于CCF-CSP第35认证中的第三,目前公开的具体目描述尚未广泛传播于网络资源中[^1]。然而,基于以往CSP认证的特点以及考生反馈的信息,可以推测该类目通常考察的是编程基础能力和逻辑思维能力[^2]。 #### 可能涉及的知识点 此类目往往围绕以下几个方面展开: - **数据结构**:如数组、链表、栈、队列等基本操作。 - **算法设计**:包括但不限于贪心算法、动态规划、二分查找等常见方法。 - **字符串处理**:对于输入输出格式的要求较高,需注意边界条件和特殊情况下的行为。 下面给出一个假设性的例子来说明如何解答类似的竞赛问: 假定本是一个简单的区间覆盖或者路径优化问,则解决方案如下所示: ```cpp #include <bits/stdc++.++.h> using namespace std; int main(){ int n; cin >> n; // 输入n表示有多少个区域或节点 vector<pair<int, int>> intervals(n); for(auto &p : intervals){ cin >> p.first >> p.second; // 每组两个数代表起点终点坐标 } sort(intervals.begin(), intervals.end()); long long res = 0; int last_end = INT_MIN; for(const auto &[start,end]:intervals){ if(start>last_end){ res += end-start; last_end=end; } else{ if(end>=last_end){ res+=end-last_end; last_end=end; } } } cout<<res<<endl;// 输出最终结果即总长度或其他目标函数值 } ``` 上述代码片段展示了当面对连续区间的合并计算时的一种通用策略——通过排序并遍历所有给定区间实现最优求解过程[^3]。 #### 注意事项 在参加正式比赛前务必熟悉评测环境配置参数限制情况(时间复杂度O()要求),同时也要关注细节部分比如是否有负权边存在等问影响到具体实现方式的选择上。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值