传送门
题意:给一棵树,每条边有权.求一条路径,权值和等于K,且边的数量最小。
思路:
考虑点分治如何合并。
我们利用树形dpdpdp求树的直径的方法,边dfsdfsdfs子树边统计答案即可。
代码:
#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
typedef pair<int,int> pii;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
const int N=2e5+5,K=1e6+5,inf=0x3f3f3f3f;
int n,k,siz[N],msiz[N],ans=inf,top=0,rt,all;
bool vis[N];
vector<pii>e[N];
void findroot(int p,int fa){
siz[p]=1,msiz[p]=0;
for(ri i=0,v;i<e[p].size();++i){
if((v=e[p][i].fi)==fa||vis[v])continue;
findroot(v,p),siz[p]+=siz[v],msiz[p]=max(msiz[p],siz[v]);
}
msiz[p]=max(msiz[p],all-siz[p]);
if(msiz[p]<msiz[rt])rt=p;
}
struct DP{
int a[K],stk[K],top;
bool in[K];
inline int idx(int k){return in[k]?a[k]:inf;}
inline void insert(int x,int v){
if(in[x])a[x]=min(a[x],v);
else stk[++top]=x,in[x]=1,a[x]=v;
}
inline pii delet(){return in[stk[top]]=0,--top,pii(stk[top+1],a[stk[top+1]]);}
inline void clear(){while(top)delet();}
}f,g;
void solve(int p,int fa,int dep,int dist){
if(dist>k)return;
g.insert(dist,dep);
ans=min(ans,min(dep+f.idx(k-dist),(dist==k)?dep:inf));
for(ri i=0,v;i<e[p].size();++i){
if((v=e[p][i].fi)==fa||vis[v])continue;
solve(v,p,dep+1,dist+e[p][i].se);
}
}
void dfs(int p){
vis[p]=1;
for(ri i=0,v;i<e[p].size();++i){
if(vis[v=e[p][i].fi])continue;
solve(v,p,1,e[p][i].se);
while(g.top){
pii tmp=g.delet();
f.insert(tmp.fi,tmp.se);
}
}
f.clear();
for(ri i=0,v;i<e[p].size();++i){
if(vis[v=e[p][i].fi])continue;
rt=0,all=siz[v],findroot(v,0),dfs(rt);
}
}
int main(){
n=read(),k=read();
for(ri i=1,u,v,w;i<n;++i)u=read()+1,v=read()+1,w=read(),e[u].push_back(pii(v,w)),e[v].push_back(pii(u,w));
msiz[rt=0]=all=n,findroot(1,0),dfs(rt);
if(ans>n)puts("-1");
else cout<<ans;
return 0;
}