bzoj 2599(点分治)

2599: [IOI2011]Race

Time Limit: 70 Sec   Memory Limit: 128 MB
Submit: 2586   Solved: 768
[ Submit][ Status][ Discuss]

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


解题思路:数据范围是N:20w, K100w. 点分治, 我们只需考虑经过当前树根的方案. K最大只有100w, 直接开个数组CNT[x]表示与当前树根距离为x的最少边数, 然后就可以对根的子树依次dfs并更新CNT数组和答案. 复杂度是O(NlogN)


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,k,len,sug,mx,root,ans,tail,head;
int to[410000],next[410000],zhi[410000],h[410000];
int size[210000],sg[1100000];
bool b[210000];
struct ss
  {
     int di,zhi;
  }q[210000];
const int ll=1000000000;
  
inline int read()
{
     char y; int x=0,f=1; y= getchar ();
     while (y< '0' || y> '9' ) { if (y== '-' ) f=-1; y= getchar ();}
     while (y>= '0' && y<= '9' ) {x=x*10+ int (y)-48; y= getchar ();}
     return x*f;
}
  
void insert( int x, int y, int z)
  {
     ++len; to[len]=y; next[len]=h[x]; zhi[len]=z; h[x]=len;
  }
  
void find( int now, int from)
  {
     int pan=-1;
     size[now]=1; int u=h[now];
     while (u!=0)
      {
         if (to[u]!=from && b[to[u]])
          {
             find(to[u],now); pan=max(pan,size[to[u]]);
             size[now]+=size[to[u]];
          }
         u=next[u];
       }
     pan=max(pan,sug-size[now]);
     if (pan<mx) mx=pan,root=now;
   }
  
void dfs( int now, int from, int shu, int step)
  {
      if (shu>k) return ;
     if (shu==k)
      {
         ans=min(ans,step);
      } else
       {
        ans=min(ans,step+sg[k-shu]);
        ++tail; q[tail].zhi=step; q[tail].di=shu;
       }
     int u=h[now];
     while (u!=0)
      {
         if (to[u]!=from && b[to[u]])
          {
             dfs(to[u],now,shu+zhi[u],step+1);
           }
         u=next[u];
      }
  }
  
void solve( int now)
  {
     b[now]= false ;
     int u=h[now];
     tail=head=0;
     while (u!=0)
      {
        if (b[to[u]])
         dfs(to[u],now,zhi[u],1);
        for ( int i=head+1;i<=tail;++i)
         sg[q[i].di]=min(sg[q[i].di],q[i].zhi);
        head=tail;
        u=next[u];
      }
     for ( int i=1;i<=tail;++i)
      sg[q[i].di]=ll;
     u=h[now];
     while (u!=0)
      {
         if (b[to[u]])
          {
            sug=size[to[u]]; mx=ll;
            find(to[u],-1);
            solve(root);
          }
         u=next[u];
       }
  }
  
int main()
{
     n=read(); k=read();
     for ( int i=1;i<=k;++i)
      sg[i]=ll;
     memset (b, true , sizeof (b));
     for ( int i=1;i<=n-1;++i)
      {
         int x,y,z;
         x=read(); y=read(); z=read();
         insert(x,y,z); insert(y,x,z);
      }
     ans=ll;
     sug=n; mx=ll, root=-1;
     find(0,-1);
     solve(root);
     if (ans==ll) printf ( "-1" ); else printf ( "%d" ,ans);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值