BZOJ2599:[IOI2011]Race(点分治)

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

Solution

开一个100W的数组t,t[i]表示到当前处理的树的根距离为i的最小边数
对于点x,我们要统计经过x的路径的话
就分别统计x的每颗子树,在统计一颗子树的时候用t[i]更新答案
并在每统计完一颗子树后更新t数组
↑这样是为了防止统计答案的时候两个点在同一子树里

Code

  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdio>
  4 #define N (200000+100)
  5 using namespace std;
  6 struct node
  7 {
  8     int to,next,len;
  9 }edge[N*2];
 10 int n,k,sum,root,ans,INF;
 11 int head[N],num_edge;
 12 int depth[N],d[N],size[N],maxn[N];
 13 int dis[N],t[N*5];
 14 bool vis[N];
 15 
 16 void add(int u,int v,int l)
 17 {
 18     edge[++num_edge].to=v;
 19     edge[num_edge].len=l;
 20     edge[num_edge].next=head[u];
 21     head[u]=num_edge;
 22 }
 23 
 24 void Get_root(int x,int fa)
 25 {
 26     size[x]=1; maxn[x]=0;
 27     for (int i=head[x];i!=0;i=edge[i].next)
 28         if (edge[i].to!=fa && !vis[edge[i].to])
 29         {
 30             Get_root(edge[i].to,x);
 31             size[x]+=size[edge[i].to];
 32             maxn[x]=max(maxn[x],size[edge[i].to]);
 33         }
 34     maxn[x]=max(maxn[x],sum-size[x]);
 35     if (maxn[x]<maxn[root]) root=x;
 36 }
 37 
 38 void Calc(int x,int fa)
 39 {
 40     if (dis[x]<=k) ans=min(ans,depth[x]+t[k-dis[x]]);
 41     for (int i=head[x];i!=0;i=edge[i].next)
 42         if (!vis[edge[i].to] && edge[i].to!=fa)
 43         {
 44             dis[edge[i].to]=dis[x]+edge[i].len;
 45             depth[edge[i].to]=depth[x]+1;
 46             Calc(edge[i].to,x);
 47         }
 48 }
 49 
 50 void Reset(int x,int fa,int flag)
 51 {
 52     if (dis[x]<=k)
 53     {
 54         if (flag) t[dis[x]]=min(t[dis[x]],depth[x]);
 55         else t[dis[x]]=INF;
 56     }
 57     for (int i=head[x];i!=0;i=edge[i].next)
 58         if (edge[i].to!=fa && !vis[edge[i].to])
 59             Reset(edge[i].to,x,flag);
 60 }
 61 
 62 void Solve(int x)
 63 {
 64     vis[x]=true; t[0]=0;
 65     for (int i=head[x];i!=0;i=edge[i].next)
 66         if (!vis[edge[i].to])
 67         {
 68             depth[edge[i].to]=1;
 69             dis[edge[i].to]=edge[i].len;
 70             Calc(edge[i].to,0);
 71             Reset(edge[i].to,0,1);
 72         }
 73     for (int i=head[x];i!=0;i=edge[i].next) 
 74         if (!vis[edge[i].to])
 75             Reset(edge[i].to,0,0);
 76     for (int i=head[x];i!=0;i=edge[i].next)
 77         if (!vis[edge[i].to])
 78         {
 79             sum=size[edge[i].to];
 80             root=0;
 81             Get_root(edge[i].to,0);
 82             Solve(root);
 83         }
 84     
 85 }
 86 
 87 int main()
 88 {
 89     int u,v,l;
 90     memset(t,0x3f,sizeof(t));
 91     memset(&INF,0x3f,sizeof(INF));
 92     scanf("%d%d",&n,&k);
 93     for (int i=1;i<=n-1;++i)
 94     {
 95         scanf("%d%d%d",&u,&v,&l);
 96         u++; v++;
 97         add(u,v,l); add(v,u,l);
 98     }
 99     ans=sum=maxn[0]=n;
100     Get_root(1,0);
101     Solve(root);
102     printf("%d",ans==n?-1:ans);
103 }

转载于:https://www.cnblogs.com/refun/p/8684117.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值