2019.01.09 bzoj2599: [IOI2011]Race(点分治)

传送门
题意:给一棵树,每条边有权.求一条路径,权值和等于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;
}

转载于:https://www.cnblogs.com/ldxcaicai/p/10367775.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值