• [IOI2011]Race


    2599: [IOI2011]Race

    Time Limit: 70 Sec  Memory Limit: 128 MB
    http://www.lydsy.com/JudgeOnline/problem.php?id=2599

    Description

    给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000

    Input

    第一行 两个整数 n, k
    第二..n行 每行三个整数 表示一条无向边的两端和权值 (注意点的编号从0开始)

    Output

    一个整数 表示最小边数量 如果不存在这样的路径 输出-1

    Sample Input

    4 3
    0 1 1
    1 2 2
    1 3 4

    Sample Output

    2
    点分治
    是求两点间边权和<=k的数量
    这里是求两点间边权和=k的最少边数
    我们仍然可以借用上题的方法,多记录一个节点到根节点的经过的边数即可
    有2个地方需要修改:
    ① 上题需要减去同一子树中不合法的点对个数
        本题虽不需要考虑同一子树内的情况,但需要在计算时跳过同一子树内的2个点
        具体做法是 在递归记录点与根节点间权值和、边数时,顺带记录每个点属于 当前根节点的哪颗子树
         计算时,如果属于同一子树,跳过
        代码:
      if(fa==head||!fa) deep[deep[0].edge_sum].id=x;

      else deep[deep[0].edge_sum].id=deep[deep[0].edge_sum-1].id;

      当前点为x,head表示当前根节点下的哪颗子树,fa表示x的父节点,id记录当前点属于哪颗子树

    上题能不能采用同样的方法,避免计算子树内部的情况呢?

    不能。因为排序仅按边权大小排,累计答案的方式是加r-l,即一堆满足条件的点判断一次,一起累加。

    判断的两个点在同一子树内,其他的点可能不在同一子树内

     ②统计答案的时候,仍然可以同上题一样采用两边指针向中间逼近的方式

       但要特殊处理指针指向位置周围边权相等的情况

     

    #include<cstdio>
    #include<algorithm>
    #define N 200001 
    using namespace std;
    int n,k,tot,sum,root,son[N],f[N],d[N],ans=N;
    int front[N],to[N*2],next[N*2],w[N*2];
    bool v[N];
    struct node
    {
        int dis,edge_sum,id;
    }deep[N];
    void getroot(int x,int fa)
    {
        son[x]=1;f[x]=0;
        for(int i=front[x];i;i=next[i])
        {
            if(to[i]==fa||v[to[i]]) continue;
            getroot(to[i],x);
            son[x]+=son[to[i]];
            f[x]=max(f[x],son[to[i]]);
        }
        f[x]=max(f[x],sum-son[x]);
        if(f[x]<f[root]) root=x;
    }
    void getdeep(int head,int x,int fa,int edge_sum)
    {
        deep[++deep[0].edge_sum]=(node){d[x],edge_sum};
        if(fa==head||!fa) deep[deep[0].edge_sum].id=x;
        else deep[deep[0].edge_sum].id=deep[deep[0].edge_sum-1].id;
        for(int i=front[x];i;i=next[i])
        {
            if(v[to[i]]||to[i]==fa) continue;
            d[to[i]]=d[x]+w[i];
            getdeep(head,to[i],x,edge_sum+1);
        }
    }
    bool cmp(node l,node r)
    {
        /*if(l.dis!=r.dis) return l.dis<r.dis;
        return l.edge_sum>r.edge_sum;*/
        return l.dis<r.dis;
    }
    void cal(int x,int now)
    {
        d[x]=now;deep[0].edge_sum=0;
        getdeep(x,x,0,0);
        int l=1,r=deep[0].edge_sum,t=0;
        sort(deep+1,deep+r+1,cmp);
        while(l<r)
        {
            /*if(deep[l].dis+deep[r].dis==k&&deep[l].id!=deep[r].id) 
            {
                ans=min(ans,deep[l].edge_sum+deep[r].edge_sum);
                //printf("%d %d
    ",deep[l].dis,deep[r].dis);    
                l++;
             
            }*/   //  错误的 
            if(deep[l].dis+deep[r].dis==k)
            {
                int p1=l,p2=r;
                while(deep[p1].dis+deep[r].dis==k) p1++;p1--;
                while(deep[p2].dis+deep[l].dis==k) p2--;p2++;
                for(int i=l;i<=p1;i++)
                 for(int j=p2;j<=r;j++)
                  if(deep[i].id!=deep[j].id)
                   ans=min(ans,deep[i].edge_sum+deep[j].edge_sum); 
                l=p1+1;r=p2-1;      
            }
            else if(deep[l].dis+deep[r].dis<k) l++;
            else r--;
        }
    }
    void work(int x)
    {
        cal(x,0);
        v[x]=true;
        for(int i=front[x];i;i=next[i])
        {
            if(v[to[i]]) continue;
            sum=son[to[i]];
            root=0;
            getroot(to[i],root);
            work(root);
        }
    }
    void add(int u,int v,int val)
    {
        to[++tot]=v;next[tot]=front[u];front[u]=tot;w[tot]=val;
        to[++tot]=u;next[tot]=front[v];front[v]=tot;w[tot]=val;
    }
    int read()
    {
        int x=0,f=1;char c=getchar();
        while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
        while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    int main()
    {
        n=read();k=read();
        int x,y,z;
        for(int i=1;i<n;i++)
        {
            x=read();y=read();z=read();
            x++;y++;
            add(x,y,z);
        }
        f[0]=N;
        sum=n;
        getroot(1,0);
        work(root);
        printf("%d",ans==N ? -1:ans);
    }
  • 相关阅读:
    02.替换空格 (Java)
    01.二维数组中的查找 (Java)
    css
    CSS Selectors
    Golang Singleton
    TL;DR
    go get
    golang string、int、int64 float 互相转换
    Thrift支持的基本数据类型
    双亲委派模型
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/6557081.html
Copyright © 2020-2023  润新知