Robots (遍历树处理 ,分治)

The Robotics Olympiad teams were competing in a contest.

There was a tree drawn on the floor, consisting of n nodes and n - 1 edges. The nodes are numbered from 1 to n, and each edge has a weight. The tree is rooted at the first node. q teams are participating, and each team is given an integer xi. Their robot should start at node 1, and move in the following way until there are no valid moves left: From all the edges between the current node and it's children, go through the edge with the maximum value less than xi. Note that the robot can't move to the parent, only to children.

However, the teams weren't able to program the robots to return to them after the contest, so they had to manually pick them up. Since the tree can be quite large, they need your help to determine where each robot ended it's movement.

Input

The first line contains a single integer T, the number of test cases.

The first line of each test case contains two space-separated integers n and q. (1 ≤ n, q ≤ 105).

The following n - 1 lines contain 3 integers ui, vi, wi. This means that there is an edge connecting nodes ui and vi, with weight wi. (1 ≤ ui, vi ≤ n) (1 ≤ wi ≤ 109). It's guaranteed that all wi are distinct.

The following line contains q integers xi. (1 ≤ xi ≤ 109).

Output

For each test case, print one line with a single number Si, the sum of numbers of nodes where each robot ends.

题意:T组数据,每组数据先给出一个n(树的节点数),一个k(机器人的数量)。然后 n-1 行每行给出两个节点和一个边权。

然后给出k个机器人的能量,刚开始所有机器人都在树根(1节点),每个机器人只能走比他小且最接近它的能量的边,无法移动时停在该节点,求所有机器人停在的节点和。

思路:因为机器人只能走把它能量值小且最接近它的能量值的边,所有我们先对所有的能量进行从小到大的排序,然后我们建树的时候,让最小的边在左边。这样的话在对每一条路选择的时候就可以直接知道哪些机器人走哪一条边了,我们只需要递归传递机器人的第一个和最后一个的索引即可。

代码如下:

#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
#include<vector>
#include<string>
#include<map>
#include<set>
#include<cmath>
#include<algorithm>
#define inf 0x3f3f3f3f
#define ll unsigned long long
using namespace std;
const double pi=acos(-1.0);
struct node
{
    int v,c;
    node (int i=0,int j=0)
    {
        v=i;
        c=j;
    }
};
int n,m,flag,a[100010],book[100010];
ll sum;
vector<node>Q[100010];
bool cmp(node x,node y)
{
    return x.c<y.c;
}
void dfs(int x,int l,int r)
{
    if(l>r)return; //这个区间已经没有机器人了
    int len=Q[x].size();
    int maxx=0,q;
    sort(Q[x].begin(),Q[x].end(),cmp);//对树的边进行排序
    int i=0;
    for(int j=0; j<len; j++) //去重,把用过的边去掉
    {
        node p=Q[x][j];
        if(!book[p.v])
            Q[x][i++]=Q[x][j];
    }
    len=i;
    i=0;
    while(l<=r&&i<len)  //枚举比这个节点的边权最小值的所有机器人
    {
        node p=Q[x][i];
        if(book[p.v])  //用过了
        {
            i++;
            continue;
        }
        if(a[l]<=p.c)
        {
            sum+=x;  //这个机器人只能停在这个点了
            l++;    //下一个机器人
        }
        else break;
    }
    for(; i<len&&l<=r; i++) //继续枚举这个节点的边权
    {
        node p=Q[x][i];
        if(book[p.v])continue;
        if(i<len-1)  //不是最后一条边
        {
            int j=i+1;
            node q=Q[x][j];  //那就要和下一条边比较了
            if(a[l]<=q.c)
            {
                int R=l;
                while(R<=r&&a[R]<=q.c) //找出符合条件的机器人
                    R++;
                book[p.v]=1;
                dfs(p.v,l,R-1);  //递归继续进行
                l=R;
            }
            else book[p.v]=1;
        }
        else  //最后一条边,剩下的机器人都符合条件
        {
            book[p.v]=1;
            dfs(p.v,l,r);
            l=r+1;
        }
    }
    if(l<=r)  //叶子节点
    {
        ll as=(ll)(r-l+1);
        ll sa=(ll)x;
        sum+=as*sa;
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0; i<100010; i++)
            Q[i].clear();
        for(int i=1; i<n; i++)
        {
            int u,v,c;
            scanf("%d%d%d",&u,&v,&c);   //建树
            Q[u].push_back(node(v,c));
            Q[v].push_back(node(u,c));
        }
        sum=0;
        for(int i=1; i<=m; i++)
            scanf("%d",&a[i]);
        sort(a+1,a+m+1);    //能量从小到大排序
        memset(book,0,sizeof(book));
        book[1]=1;
        dfs(1,1,m);  //一号节点,第一个到最后一个机器人
        printf("%llu\n",sum);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值