• 【JZOJ4715】【NOIP2016提高A组模拟8.19】树上路径


    题目描述

    给出一棵树,求出最小的k,使得,且在树中存在路径p,使得k>=S且k<=E。(k为路径p上的边的权值和)

    输入

    第一行给出N,S,E。N代表树的点数,S,E如题目描述。
    下面N-1行给出这棵树的相邻两个节点的边及其权值W。

    输出

    输出共一行一个整数,表示答案。若无解输出-1。

    样例输入

    5 10 40
    2 4 80
    2 3 57
    1 2 16
    2 5 49

    样例输出

    16

    样例解释

    1到2的路径即为答案。

    数据范围

    对于20%的数据满足n<=300
    对于50%的数据满足n<=3000
    对于60%的数据满足n<=10^5
    对于以上数据,满足|E-S|<=50
    对于100%的数据满足n<=10^5,|E-S|<=10^6
    对于所有数据满足1<=Wi<=1000,|E|,|S|<=10^9

    解法

    树上路径问题使用树分治解决。
    对于当前树,维护一个Dis数组表示,当前树中的每个点到当前树根结点的距离。
    依次遍历当前根结点的子树,得出来一个dis表示这个子树的结点到当前根结点的距离。考虑合并Dis和dis,对于每个dis[i],在Dis中二分出一个Dis[j]使得Dis[j]+dis[i]>=S,然后更新答案。
    最后把dis并入Dis维护有序性即可。

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define ll long long
    #define sqr(x) ((x)*(x))
    #define ln(x,y) int(log(x)/log(y))
    using namespace std;
    const char* fin="aP2.in";
    const char* fout="aP2.out";
    const int inf=0x7fffffff;
    const int maxn=100007,maxm=2*maxn,maxde=31;
    int n,m1,m2,i,j,k,l,tot,siz,root,ans=inf;
    int fi[maxn],la[maxm],va[maxm],ne[maxm];
    int dis[maxn],Dis[maxn];
    bool bz[maxn];
    void add_line(int a,int b,int c){
        tot++;
        ne[tot]=fi[a];
        la[tot]=b;
        va[tot]=c;
        fi[a]=tot;
    }
    void getsize(int v,int from){
        int i,j,k;
        if (!from) siz=0;
        siz++;
        for (k=fi[v];k;k=ne[k]) if (!bz[la[k]] && la[k]!=from) getsize(la[k],v);
    }
    int getroot(int v,int from){
        int i=1,j=1,k,tmp;
        for (k=fi[v];k;k=ne[k]){
            if (!bz[la[k]] && la[k]!=from) {
                tmp=getroot(la[k],v);
                if (tmp>siz/2) i=0;
                j+=tmp;
            }
        }
        if (i && j>siz/2) root=v;
        return j;
    }
    void getdis(int v,int from,int st){
        int i,j,k;
        if (st>m2) return;
        dis[++dis[0]]=st;
        for (k=fi[v];k;k=ne[k]){
            if (la[k]!=from && !bz[la[k]]){
                getdis(la[k],v,st+va[k]);
            }
        }
    }
    void merge(){
        int i,j,k,l,r,mid;
        for (i=1;i<=dis[0];i++){
            l=1;
            r=Dis[0];
            while (l<r){
                mid=(l+r)/2;
                if (dis[i]+Dis[mid]>=m1) r=mid;
                else l=mid+1;
            }
            if (dis[i]+Dis[l]>=m1) ans=min(ans,dis[i]+Dis[l]);
        }
        for (i=1;i<=dis[0];i++) Dis[++Dis[0]]=dis[i];
        sort(Dis+1,Dis+Dis[0]+1);
    }
    void dfs(int v,int de){
        int i,j,k;
        Dis[0]=1;
        Dis[1]=0;
        bz[v]=true;
        for (k=fi[v];k;k=ne[k]){
            if (!bz[la[k]]){
                dis[0]=0;
                getdis(la[k],v,va[k]);
                merge();
            }
        }
        for (k=fi[v];k;k=ne[k])
            if (!bz[la[k]]){
                getsize(la[k],0);
                getroot(la[k],0);
                dfs(la[k],de+1);
            }
    }
    int main(){
        scanf("%d%d%d",&n,&m1,&m2);
        for (i=1;i<n;i++){
            scanf("%d%d%d",&j,&k,&l);
            add_line(j,k,l);
            add_line(k,j,l);
        }
        getsize(1,0);
        getroot(1,0);
        dfs(1,0);
        if (ans>m2) printf("-1");
        else printf("%d",ans);
        return 0;
    }

    启发

    树上路径问题使用树上分治。
    树上分治处理方法并不单一,须灵活运用。

  • 相关阅读:
    Oracle 添加主键和索引
    Oracle中查询主键、外键、sequence、表基本信息等
    Spring工作原理
    Ehcache 缓存使用
    socket编程-java
    oracle触发器详解
    单例模式的几种写法
    [LeetCode] 412. Fizz Buzz 嘶嘶嗡嗡
    LeetCode Top Interview Questions
    [LeetCode] 131. Palindrome Partitioning 回文分割
  • 原文地址:https://www.cnblogs.com/hiweibolu/p/6714910.html
Copyright © 2020-2023  润新知