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

题目描述

给一棵树,每条边有权。求一条简单路径,权值和等于$K$ ,且边的数量最小。

输入输出格式

输入格式:

第一行:两个整数$n,k$

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

输出格式:

一个整数,表示最小边数量。

如果不存在这样的路径,输出 -11 。

输入输出样例

输入样例#1: 复制
4 3
0 1 1
1 2 2
1 3 4
输出样例#1: 复制
2

说明

$n\le 200000,K\le 1000000$

题解

  我们用一个数组$f[j]$表示点分到当前子树中时路径长度为$j$的最小边数

  然后每一棵子树里去$dfs$一遍更新$f$数组,然后用$f$数组相加更新答案

  复杂度为$O(nlogn)$

 1 //minamoto
 2 #include<cstdio>
 3 #include<iostream>
 4 #define ll long long
 5 using namespace std;
 6 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
 7 char buf[1<<21],*p1=buf,*p2=buf;
 8 template<class T>inline int cmax(T&a,const T&b){return a<b?a=b,1:0;}
 9 template<class T>inline int cmin(T&a,const T&b){return a>b?a=b,1:0;}
10 inline int read(){
11     #define num ch-'0'
12     char ch;bool flag=0;int res;
13     while(!isdigit(ch=getc()))
14     (ch=='-')&&(flag=true);
15     for(res=num;isdigit(ch=getc());res=res*10+num);
16     (flag)&&(res=-res);
17     #undef num
18     return res;
19 }
20 char sr[1<<21],z[20];int C=-1,Z;
21 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
22 inline void print(int x){
23     if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
24     while(z[++Z]=x%10+48,x/=10);
25     while(sr[++C]=z[Z],--Z);sr[++C]='\n';
26 }
27 const int N=200005,K=1000005;
28 int ver[N<<1],head[N],Next[N<<1],edge[N<<1];
29 int n,k,ans,tot,size,sz[N],son[N],rt,cnt[K];bool vis[N];ll d[N];
30 inline void add(int u,int v,int e){
31     ver[++tot]=v,Next[tot]=head[u],head[u]=tot,edge[tot]=e;
32     ver[++tot]=u,Next[tot]=head[v],head[v]=tot,edge[tot]=e;
33 }
34 void findrt(int u,int fa){
35     sz[u]=1,son[u]=0;
36     for(int i=head[u];i;i=Next[i]){
37         int v=ver[i];if(v==fa||vis[v]) continue;
38         findrt(v,u),sz[u]+=sz[v],cmax(son[u],sz[v]);
39     }
40     cmax(son[u],size-sz[u]);
41     if(son[u]<son[rt]) rt=u;
42 }
43 void dfs(int u,int fa,int dep){
44     if(d[u]>=0&&d[u]<=k) cmin(ans,dep+cnt[k-d[u]]);
45     for(int i=head[u];i;i=Next[i]){
46         int v=ver[i];
47         if(v!=fa&&!vis[v]){
48             d[v]=d[u]+edge[i],dfs(v,u,dep+1);
49         }
50     }
51 }
52 void update(int u,int fa,int dep,int opt){
53     if(d[u]>=0&&d[u]<=k)
54     opt?cmin(cnt[d[u]],dep):cnt[d[u]]=n;
55     for(int i=head[u];i;i=Next[i]){
56         int v=ver[i];
57         if(!vis[v]&&v!=fa)
58         update(v,u,dep+1,opt);
59     }
60 }
61 void solve(int u){
62     vis[u]=true,cnt[0]=0;
63     for(int i=head[u];i;i=Next[i]){
64         int v=ver[i];
65         if(!vis[v])
66         d[v]=edge[i],dfs(v,0,1),update(v,0,1,1);
67     }
68     for(int i=head[u];i;i=Next[i]){
69         int v=ver[i];
70         if(!vis[v])
71         update(v,0,1,0);
72     }
73     int totsz=size;
74     for(int i=head[u];i;i=Next[i]){
75         int v=ver[i];
76         if(!vis[v]){
77             rt=0,size=sz[v];
78             findrt(v,0),solve(rt);
79         }
80     }
81 }
82 int main(){
83     n=read(),k=read(),ans=n;
84     for(int i=1;i<n;++i){
85         int u=read()+1,v=read()+1,e=read();add(u,v,e);
86     }
87     for(int i=1;i<=k;++i) cnt[i]=n;
88     son[rt=0]=n+1,size=n,findrt(1,0),solve(rt);
89     print(ans==n?-1:ans);
90     Ot();
91     return 0;
92 }

 

posted @ 2018-08-16 15:43  bztMinamoto  阅读(431)  评论(0编辑  收藏  举报
Live2D