【BZOJ】2599 [IOI2011]Race 点分治

80 篇文章 1 订阅
3 篇文章 0 订阅

题目传送门

这题是一道有点难的点分治,但是我的想法和标算好像有些出入诶……

考虑点分治的一般套路,都是先求树的重心,然后计算当前子树中所有非树根节点到树根节点的距离,最后根据题目要求统计答案。

这题嘛,还是一样的套路,对于当前子树,记录 所有已经搜索过的非树根节点 到树根的距离 所需的最小深度。(这句话要好好理解)

然后在统计答案时,就用当前节点的深度加上k减去当前节点到树根的距离所需的最小深度来更新答案即可。

但是对于同一子树间的判重,我还不是弄得很清楚,需要反复思考。

等我弄懂之时,再来更新吧。

附上AC代码:

#include <cstdio>
#include <cctype>
#define N 200010
#define INF 2e9
using namespace std;

struct side{
	int to,w,nt;
}s[N<<1];
struct note{
	int dep,dis;
}d[N];
int n,m,x,y,w,num,h[N],mx[N],ans,sum,t[1000010],rt,size[N],dis[N],dep[N],top,pre;
bool b[N];

inline char nc(){
	static char ch[100010],*p1=ch,*p2=ch;
	return p1==p2&&(p2=(p1=ch)+fread(ch,1,100010,stdin),p1==p2)?EOF:*p1++;
}

inline void read(int &a){
	static char c=nc();int f=1;
	for (;!isdigit(c);c=nc()) if (c=='-') f=-1;
	for (a=0;isdigit(c);a=a*10+c-'0',c=nc());
	a*=f;return;
}

inline void add(int x,int y,int w){
	s[++num]=(side){y,w,h[x]},h[x]=num;
	s[++num]=(side){x,w,h[y]},h[y]=num;
}

inline int max(int a,int b){
	return a>b?a:b;
}

inline int min(int a,int b){
	return a<b?a:b;
}

inline void so(int x,int fa){
	size[x]=1,mx[x]=0;
	for (int i=h[x]; i; i=s[i].nt)
		if (!b[s[i].to]&&s[i].to!=fa)	
			so(s[i].to,x),size[x]+=size[s[i].to],mx[x]=max(mx[x],size[s[i].to]);
	mx[x]=max(mx[x],sum-size[x]);
	if (mx[x]<mx[rt]) rt=x;
	return;
}

inline void get(int x,int fa){
	if (dis[x]>m) return;
	d[++top].dep=dep[x],d[top].dis=dis[x];
	ans=min(ans,dep[x]+t[m-dis[x]]);
	for (int i=h[x]; i; i=s[i].nt)
		if (!b[s[i].to]&&s[i].to!=fa){
			dis[s[i].to]=dis[x]+s[i].w,dep[s[i].to]=dep[x]+1;
			if (dis[s[i].to]<=m) get(s[i].to,x);
		}
	return;
}

inline void calc(int x){
	dis[x]=dep[x]=top=0,pre=1;
	for (int i=h[x]; i; i=s[i].nt)
		if (!b[s[i].to]){
			dep[s[i].to]=1,dis[s[i].to]=s[i].w,get(s[i].to,x);
			for (int j=pre; j<=top; ++j)
				if (d[j].dis<=m) t[d[j].dis]=min(t[d[j].dis],d[j].dep);
			pre=top+1;
		}
	for (int i=1; i<=top; ++i)
		if (d[i].dis<=m) t[d[i].dis]=INF;
	t[0]=0;return;
}

inline void work(int x){
	calc(x),b[x]=1;
	for (int i=h[x]; i; i=s[i].nt)
		if (!b[s[i].to])
			sum=size[s[i].to],rt=0,so(s[i].to,0),work(rt);
	return;
}

int main(void){
	read(n),read(m);
	for (int i=1; i<n; ++i) read(x),++x,read(y),++y,read(w),add(x,y,w);
	mx[0]=ans=INF,sum=n;
	for (int i=1; i<=m; ++i) t[i]=INF;
	so(1,0),work(rt);
	printf("%d",ans==INF?-1:ans);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值