BZOJ 2599: [IOI2011]Race【点分治】

题面:

给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000

题目分析:

点分治模板题。
解决一个重心的流程大概如下:
(假设当前子树的重心为u,解决u子树内的子问题, f [ i ] f[i] f[i]表示权值为i的最小边数)

  • 遍历u的儿子v1,把对应的边数和权值放入栈中(边数>ans或权值>k return),并用边数+f[k-权值]更新答案。
  • 在遍历结束后用栈中的元素更新f数组,继续下一个儿子的遍历。
  • 所有儿子遍历结束后把所有栈中元素对应的f置为n,表示消除影响。

Code:

#include<cstdio>
#include<cctype>
#include<algorithm>
#define maxn 200005
using namespace std;
char cb[1<<16],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<16,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
	char c;while(!isdigit(c=getc()));
	for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
int n,k,siz[maxn],f[1000005],ans=maxn,st[maxn][2],top;
int fir[maxn],nxt[maxn<<1],to[maxn<<1],w[maxn<<1],tot;
bool ban[maxn];
inline void line(int x,int y,int z){nxt[++tot]=fir[x];fir[x]=tot;to[tot]=y;w[tot]=z;}
void calc(int u,int pre,int dep,int dis){
	if(dis>k||dep>=ans) return;
	ans=min(ans,dep+f[k-dis]);
	st[++top][0]=dep,st[top][1]=dis;
	for(int i=fir[u];i;i=nxt[i]) if(!ban[to[i]]&&to[i]!=pre) calc(to[i],u,dep+1,dis+w[i]);
}
void solve(int u,int pre){
	for(int i=fir[u],v;i;i=nxt[i]) if(!ban[v=to[i]]&&v!=pre){
		int cur=top;
		calc(v,u,1,w[i]);
		for(int j=cur+1;j<=top;j++) f[st[j][1]]=min(f[st[j][1]],st[j][0]);
	}
	while(top) f[st[top--][1]]=n;
}
void getroot(int u,int pre,int tsz,int &G){
	siz[u]=1; bool flg=1;
	for(int i=fir[u],v;i;i=nxt[i]) if(!ban[v=to[i]]&&v!=pre){
		getroot(v,u,tsz,G),siz[u]+=siz[v];
		if(siz[v]<<1 > tsz) flg=0;
	}
	if((tsz-siz[u])<<1 > tsz) flg=0;
	if(flg) G=u;
}
void TDC(int u,int tsz){
	getroot(u,0,tsz,u);
	solve(u,0); ban[u]=1;
	for(int i=fir[u];i;i=nxt[i]) 
		if(!ban[to[i]]) TDC(to[i],siz[to[i]]<siz[u]?siz[to[i]]:tsz-siz[u]);
}
int main()
{
	read(n),read(k);
	for(int i=1,x,y,z;i<n;i++) read(x),read(y),read(z),x++,y++,line(x,y,z),line(y,x,z);
	for(int i=1;i<=k;i++) f[i]=n;
	TDC(1,n);
	printf("%d",ans<n?ans:-1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值