【JZOJ5898】距离统计

3 篇文章 0 订阅
3 篇文章 0 订阅

Description

给你一颗N个点有边权的树,多次询问到一个点距离第k小为多少。

Solution

考虑点分治,把点分树建出来,对于一个询问点,直接往它的点分树父亲上跳。
求第k小考虑二分,那么相当于求权值和小于等于一个值的路径条数,那么计算跨越询问点的每一个点分树上的祖先的路径数即可。这里注意一下要减去重复的部分。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
#define rep(i,x) for(int i=ls[x];i;i=nx[i])
using namespace std;
const int N=5e4+10,M=1e5+10,inf=1e9;
typedef long long ll;
int to[M],nx[M],ls[N],vl[M],num=0;
void link(int u,int v,int w){
	to[++num]=v,nx[num]=ls[u],ls[u]=num;
	vl[num]=w;
}
int sz[N],f[N],dep[N];
int fa[N][20],d[N][20];
vector<int> g[N],h[N];
int rt;
bool vis[N];
void getsz(int x,int fr,int tt){
	sz[x]=1,f[x]=0;
	rep(i,x){
		int v=to[i];
		if(v==fr || vis[v]) continue;
		getsz(v,x,tt),sz[x]+=sz[v];
		f[x]=max(f[x],sz[v]);
	}
	f[x]=max(f[x],tt-sz[x]);
	if(f[rt]>f[x]) rt=x;
}
void get(int x,int fr,int t,int now){
	sz[x]=1;
	g[now].push_back(d[x][dep[now]]);
	if(dep[now]>1) h[now].push_back(d[x][dep[now]-1]);
	rep(i,x){
		int v=to[i];
		if(v==fr || vis[v]) continue;
		d[v][dep[now]]=d[x][dep[now]]+vl[i];
		get(v,x,t,now),sz[x]+=sz[v];
	}
}
void dfs(int x,int fr){
	vis[x]=1,dep[x]=dep[fr]+1;
	fa[x][dep[x]]=x;
	fo(i,1,dep[fr]) fa[x][i]=fa[fr][i];
	g[x].push_back(0);
	if(fr) h[x].push_back(d[x][dep[fr]]);
	rep(i,x){
		int v=to[i];
		if(v==fr || vis[v]) continue;
		d[v][dep[x]]=vl[i],get(v,x,v,x);
	}
	rep(i,x){
		int v=to[i];
		if(v==fr || vis[v]) continue;
		rt=0,getsz(v,x,sz[v]);
		dfs(rt,x);
	}
}
ll calc(int x,int t){
	ll ans=0;
	fd(i,dep[x],1){
		int now=fa[x][i];
		ans+=upper_bound(g[now].begin(),g[now].end(),t-d[x][i])-g[now].begin();
		if(i<dep[x]){
			int la=fa[x][i+1];
			ans-=upper_bound(h[la].begin(),h[la].end(),t-d[x][i])-h[la].begin();
		}
	}
	return ans;
}
int main()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	int n,q;
	scanf("%d %d",&n,&q);
	fo(i,2,n){
		int u,v,w;
		scanf("%d %d %d",&u,&v,&w);
		link(u,v,w),link(v,u,w);
	}
	f[0]=n+1;
	rt=0,getsz(1,0,n);
	dep[rt]=1,dfs(rt,0);
	fo(i,1,n){
		int o=h[i].size();
		fo(j,0,o-1) printf("%d ",h[i][j]);
		printf("\n");
		sort(g[i].begin(),g[i].end()),sort(h[i].begin(),h[i].end());
		g[i].push_back(inf),h[i].push_back(inf);
	}
	while(q--){
		int u,k;
		scanf("%d %d",&u,&k),k++;
		int l=1,r=5e7;
		while(l+1<r){
			int mid=l+r>>1;
			calc(u,mid)>=k?r=mid:l=mid;
		}
		calc(u,l)>=k?r=l:0;
		printf("%d\n",r);
	}
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值