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<queue>
  4 #include<vector>
  5 #include<cstdio>
  6 #include<cstring>
  7 #include<iostream>
  8 #include<algorithm>
  9 #define trav(u,i) for(int i=front[u];i;i=e[i].nxt)
 10 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
 11 using namespace std;
 12
 13 typedef long long ll;
 14 const int N = 1e6+10;
 15 const int inf = 1e9;
 16
 17 ll read() {
 18     char c=getchar();
 19     ll f=1,x=0;
 20     while(!isdigit(c)) {
 21         if(c==‘-‘) f=-1; c=getchar();
 22     }
 23     while(isdigit(c))
 24         x=x*10+c-‘0‘,c=getchar();
 25     return x*f;
 26 }
 27
 28 struct Edge {
 29     int v,w,nxt;
 30 }e[N<<1];
 31 int en=1,front[N];
 32 void adde(int u,int v,int w)
 33 {
 34     e[++en]=(Edge){v,w,front[u]}; front[u]=en;
 35 }
 36
 37 int l1,l2;
 38 int t[N],dep[N],dis[N],ans=inf,list[N];
 39 int vis[N],siz[N],rt,f[N],size,n,K;
 40
 41 void getroot(int u,int fa)
 42 {
 43     siz[u]=1; f[u]=0;
 44     trav(u,i) if(e[i].v!=fa&&!vis[e[i].v]){
 45         int v=e[i].v;
 46         getroot(v,u);
 47         siz[u]+=siz[v];
 48         f[u]=max(f[u],siz[v]);
 49     }
 50     f[u]=max(f[u],size-siz[u]);
 51     if(f[u]<f[rt]) rt=u;
 52 }
 53 void dfs(int u,int fa)
 54 {
 55     list[++l1]=u;
 56     trav(u,i) if(e[i].v!=fa&&!vis[e[i].v]) {
 57         int v=e[i].v;
 58         dis[v]=dis[u]+e[i].w;
 59         dep[v]=dep[u]+1;
 60         dfs(v,u);
 61     }
 62 }
 63 void solve(int u)
 64 {
 65     vis[u]=1; t[0]=0;
 66     l1=l2=0;
 67     trav(u,i) if(!vis[e[i].v]) {
 68         int v=e[i].v;
 69         dep[v]=1; dis[v]=e[i].w;
 70         dfs(v,-1);
 71         FOR(j,l2+1,l1) {
 72             if(dis[list[j]]<=K)
 73                 ans=min(ans,dep[list[j]]+t[K-dis[list[j]]]);
 74         }
 75         FOR(j,l2+1,l1)
 76             if(dis[list[j]]<=K) t[dis[list[j]]]=min(t[dis[list[j]]],dep[list[j]]);
 77         l2=l1;
 78     }
 79     FOR(i,0,l1) t[dis[list[i]]]=inf;
 80     trav(u,i) if(!vis[e[i].v]) {
 81         int v=e[i].v; rt=0;
 82         getroot(v,-1); size=siz[v];
 83         solve(rt);
 84     }
 85 }
 86
 87 int main()
 88 {
 89     freopen("in.in","r",stdin);
 90     freopen("out.out","w",stdout);
 91     n=read(),K=read();
 92     int u,v,w;
 93     FOR(i,1,n-1) {
 94         u=read()+1,v=read()+1,w=read();
 95         adde(u,v,w),adde(v,u,w);
 96     }
 97     FOR(i,1,K) t[i]=n;
 98     size=f[0]=ans=n;
 99     getroot(1,-1);
100     solve(rt);
101     if(ans==n) puts("-1");
102     else printf("%d\n",ans);
103     return 0;
104 }
时间: 2024-10-06 01:52:46

bzoj 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 未调完_点分治

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 点分治 考虑如何计算答案,有一个节点,我

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]表示到当前处理的树的根距离

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(点分治)

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

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