2599. [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

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

  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

时间: 2024-10-05 05:32:33

2599. [IOI2011]Race【点分治】的相关文章

BZOJ 2599: [IOI2011]Race( 点分治 )

数据范围是N:20w, K100w. 点分治, 我们只需考虑经过当前树根的方案. K最大只有100w, 直接开个数组CNT[x]表示与当前树根距离为x的最少边数, 然后就可以对根的子树依次dfs并更新CNT数组和答案. ------------------------------------------------------------------------------------------ #include<bits/stdc++.h> using namespace std; typ

bzoj 2599 [IOI2011]Race (点分治)

[题意] 问树中长为k的路径中包含边数最少的路径所包含的边数. [思路] 统计经过根的路径.假设当前枚举到根的第S个子树,若x属于S子树,则有: ans<-dep[x]+min{ dep[y] },y属于前S-1个子树,dis[x]<=K 所以只需要用一个数组t[len]记录前S-1棵子树中长度为len的最少边数即可.t只用开到K的最大值. 然后分治处理子树. [代码] 1 #include<set> 2 #include<cmath> 3 #include<qu

bzoj 2599 [IOI2011]Race 未调完_点分治

Code: // luogu-judger-enable-o2 // luogu-judger-enable-o2 #include <bits/stdc++.h> #define setIO(s) freopen(s".in","r",stdin) #define maxn 1000000 #define inf 0x7f7f7f using namespace std; int hd[maxn],to[maxn],nex[maxn],val[maxn

【刷题】BZOJ 2599 [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 点分治 考虑如何计算答案,有一个节点,我

IOI2011 Race [点分治]

题意 给一棵树,每条边有权.求一条简单路径,权值和等于 $K$,且边的数量最小. 点分治,求距离时带上经过边的数量即可.用的第一种写法(下面). 食用淀粉质注意事项 1. 统计子树内答案的两种写法: 跟树形dp一样将某子树与前面的子树合并   或者是   考虑所有子树的答案再容斥,减去不合法的一棵子树内答案. 2.好好写求重心,千万不要写假!!!假淀粉害死人 注意每次遍历先初始化$f[x]=0$,要有子树大小$S$. 1 #include <cstdio> 2 #include <cst

P4149 [IOI2011]Race 点分治

思路: 点分治 提交:5次 题解: 刚开始用排序+双指针写的,但是调了一晚上,总是有两个点过不了,第二天发现原因是排序时的\(cmp\)函数写错了:如果对于路径长度相同的,我们从小往大按边数排序,当双指针出现\(==k\)时,即我们应先左移右指针,否则答案可能会变劣(仔细想一想):若反着排序,应该先右移左指针. #include<bits/stdc++.h> #define R register int using namespace std; namespace Luitaryi { tem

【BZOJ-2599】Race 点分治

2599: [IOI2011]Race Time Limit: 70 Sec  Memory Limit: 128 MBSubmit: 2590  Solved: 769[Submit][Status][Discuss] Description 给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000 Input 第一行 两个整数 n, k第二..n行 每行三个整数 表示一条无向边的两端和权值 (注意点的编号从0开始) Output

【BZOJ2599】[IOI2011]Race 树的点分治

[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 题解:本题大

bzoj2599: [IOI2011]Race(点分治)

写了四五道点分治的题目了,算是比较理解点分治是什么东西了吧= = 点分治主要用来解决点对之间的问题的,比如距离为不大于K的点有多少对. 这道题要求距离等于K的点对中连接两点的最小边数. 那么其实道理是一样的.先找重心,然后先从重心开始求距离dis和边数num,更新ans,再从重心的儿子开始求得dis和num,减去这部分答案 因为这部分的答案中,从重心开始的两条链有重叠部分,所以要剪掉 基本算是模板题,但是减去儿子的答案的那部分还有双指针那里调了好久,所以还不算特别熟练.. PS跑了27秒慢到飞起