zoukankan      html  css  js  c++  java
  • Happy Tree Friends(一个在树上搜索的题)

    icpc上的题:12669

    题解:我的题解是按照叶子链重小到大删叶子链,直到叶子链个数不大于k,每次删叶子链时记录删的节点权值和并且把答案减去这些删的权值和,最后还剩下的ans就是答案

    不过这个题输入的是无向边,所以你得重新建树

    问题 I: Happy Tree Friends

    时间限制: 1 Sec  内存限制: 128 MB
    提交: 41  解决: 14
    [提交] [状态] [命题人:admin]

    题目描述

    There is a Happy Tree on an island. Happy Tree consists of n apples linked by n-1 branches and the No.1 apple is the root of the tree. Each apple has a value a_i.
    Today, k friends are playing under Happy Tree. Each of them can select only one apple, then gets the sum of the values from root to the selected apple, and take all the apples on the road (including the root and the selected apple). Of course, an apple can only be taken once.
    Now, they want to know the maximum value they can get. They promise that everyone can select at least one apple.

    输入

    The first line consists of two non-negative integers, the number of apples n, and the number of friends k. ( 1 ≤ n ,k ≤2×105  )
    The second line consists of n non-negative integers, the value of the i-th apple a_i. ( 1 ≤ a_i  ≤ 109  )
    Then there are n-1 lines, each line consists of two non-negative integers, and describe a branch between u and v. ( 1≤u ,v ≤n )

    输出

    Only one integer, the maximum value they can get.

    样例输入

    5 2
    4 3 2 1 1
    1 2
    1 5
    2 3
    2 4
    

    样例输出

    10


    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=2e5+7;
    int n,k,cnt,cnt2,head[maxn],chi_nu[maxn],leaf,leafs[maxn],father[maxn],letime[maxn];
    ll ans,cost[maxn],leaf_cost[maxn];//cnt是每一次建树用的,cnt2是广搜时保存新的边用的,chi_nu[]是保存当前最新的每一个节点后面的叶子个数
    //lea是最新叶子个数,leafs[]是第一次把叶子及其有效值加入优先队列时用来保存叶子节点的,father[]是保存每一个节点的父节点
    //lettime[]是每一个叶子节点的时间戳,这样每次加入优先队列时就可以判断当前叶子节点是否最新的
    //ans保存答案,cost[]保存最初每一个节点的有效值,leaf_cost[i]保存当前最新叶子节点为i的叶子那条链的有效值
    bool vis[maxn],vis2[maxn];//vis[]是删边标记用的,vis2[]是重新建树时广搜标记用的
    struct bian{
        int a,b;
    }renew[maxn];//保存重新建树的新边用的
    struct lenode{//叶节点的最大有效值以及优先队列的节点
        int u,time=0;
        ll l_cost=0;
        bool operator<(const lenode & a)const{
            return l_cost>a.l_cost;
        }
    };
    lenode genx;//每一次删叶子节点保存的新的叶子节点及其有效值
    bool flaggenxi=0;//每一次删边时是否更新其它叶子节点的标记判断
    priority_queue<lenode>leafque;//优先队列,用以排序当前还存在的叶子节点,按有效值重小到大
    struct node{//邻接表节点
        int to,next;
    } edge[2*maxn];//邻接表
    void add(int a,int b){//邻接表加边
        edge[++cnt].to=b,edge[cnt].next=head[a];
        head[a]=cnt;
    }
    int DFS(int now){//寻找叶节点以及赋值每一个节点后面存在的叶节点数,叶节点的这个数为零
        int num=0,to;
        for(register int i=head[now]; i; i=edge[i].next){
            to=edge[i].to;
            if(head[to]==0)++num,leafs[++leaf]=to;
            else {
                chi_nu[to]=DFS(to);
                num+=chi_nu[to];
            }
        }
        return num;
    }
    ll zfa(int now){//计算叶节点的有效值
        if(now==0)return 0;
        if(chi_nu[now]<=1)return cost[now]+zfa(father[now]);
        return 0;
    }
    void zfa3(int now){//更新此节点的后面叶子个数
        if(now==0)return;
        --chi_nu[now];
        zfa3(father[now]);
    }
    int zaoyi(int now){//寻找叶节点
        int to;
        for(register int i=head[now]; i; i=edge[i].next){
            to=edge[i].to;
            if(!vis[to]){
                if(head[to]==0)return to;
                return zaoyi(to);
                break;
            }
        }
    }
    ll zfsum(int now){//当删叶节点时又得到一条新的链时往根节点查找有效值及更新当前叶节点的有效值
        if(now==0)return 0;
        if(chi_nu[now]==1)
            return cost[now]+zfsum(father[now]);
        return 0;
    }
    void zfa2(int now){//删除一个叶子节点
        if(chi_nu[now]>2){
            chi_nu[now]--;
            zfa3(father[now]);
            return;
        }
        if(chi_nu[now]==1){
            vis[now]=1;
            zfa2(father[now]);
            return;
        }
        --chi_nu[now];
        zfa3(father[now]);
        int yi=zaoyi(now);
        ll sumxs=leaf_cost[yi]+cost[now];
        sumxs+=zfsum(father[now]);
        flaggenxi=1;
        genx.l_cost=sumxs;
        genx.u=yi;
    }
    void BFS(){//第二次建树所需要的广搜,就是重新找边
        queue<int>que;
        que.push(1);
        bian temp;
        vis2[1]=1;
        while(!que.empty()){
            int now=que.front();
            que.pop();
            temp.a=now;
            for(register int i=head[now];i;i=edge[i].next){
                int to=edge[i].to;
                if(!vis2[to]){
                    vis2[to]=1;
                    que.push(to);
                    temp.b=to;
                    renew[++cnt2]=temp;
                }
            }
        }
    }
    int main(){
        scanf("%d %d",&n,&k);
        int a,b;
        for(register int i=1; i<=n; ++i)
            scanf("%lld",&cost[i]),ans+=cost[i];
        for(register int i=1; i<=n-1; ++i){//第一次建树
            scanf("%d %d",&a,&b);
            add(a,b);
            add(b,a);
        }
        BFS();//由于是双向的这个题,但是我的代码得是标准的单向树才能合理运行,所以的重新建树
        cnt=0;
        memset(head,0,sizeof(head));
        for(register int i=1;i<n;++i){//第二次重新建树
            add(renew[i].a,renew[i].b);
            father[renew[i].b]=renew[i].a;
        }
        chi_nu[1]=DFS(1);//调用函数计算每个节点后面的叶子个数
        if(leaf<=k){//leaf<=k代表可以把所有节点吃完
            printf("%lld
    ",ans);
            return 0;
        }
        lenode te;
        for(register int i=1; i<=leaf; ++i){//计算每一个叶子节点的有效值
            leaf_cost[leafs[i]]=zfa(father[leafs[i]])+cost[leafs[i]];
            te.l_cost=leaf_cost[leafs[i]];
            te.u=leafs[i];
            leafque.push(te);
        }
        while(leaf>k){
            flaggenxi=0;
            te=leafque.top();
            leafque.pop();
            while(te.time<letime[te.u]){//运用时间戳,避免了大量的查找
                te=leafque.top();
                leafque.pop();
            }
            vis[te.u]=1;
            zfa2(father[te.u]);
            ans-=te.l_cost;
            --leaf;
            if(flaggenxi){//如果在删除叶子节点时产生其它的叶子节点更新,就更新
                genx.time=++letime[genx.u];
                leafque.push(genx);
                leaf_cost[genx.u]=genx.l_cost;//更新叶子节点有效值,第一次就是调这个bug调了好就才发现没有更新
            }
        }
        printf("%lld
    ",ans);
        return 0;
    }
    /*
    8 1
    4 3 2 1 2 4 2 3
    2 1
    3 2
    6 2
    4 2
    5 1
    7 5
    8 5
    
    5 2
    4 3 2 1 1
    1 2
    1 5
    2 3
    2 4
    */
    

      

  • 相关阅读:
    JDK1.5新特性
    mysql的基本使用
    IO简单示例
    序列化
    策略模式
    div+css布局之流体浮动布局
    xp优化
    Junit所使用的设计模式
    SSH使用总结(annotation配置方式)
    hibernate3.6.0使用总结
  • 原文地址:https://www.cnblogs.com/lengsong/p/11463592.html
Copyright © 2011-2022 走看看