Live2D

题解 Children Trips

题目传送门

Description

给出一个大小为 \(n\) 的边权全为 \(1,2\) 的带权树,有 \(q\) 此查询,每次给出 \(u,v,p\) ,问 \(u\to v\) 每次可以最多走边权和 \(\le p\) 的路径,问最少走多少次。

\(n,q\le 10^5\)

Solution

因为自己没有想出来,所以还是写一发题解。

首先边权比较有迷惑性,但是这个 \(\le 2\) 确实多少用。考虑根号分治,可以发现 \(p>\sqrt n\) 最多只会爬 \(\sqrt n\) 次,所以可以直接暴力爬,即倍增找到每次可以走到的下一个点。

考虑 \(\sqrt n\),发现我们仍可以使用倍增,即处理出一个点走 \(2^i\) 次会走到哪里。

复杂度 \(\Theta(n\sqrt n\log n)\)

Code

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define MAXN 100005

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}

int n,m,siz;

struct edge{int v,w;}; 
vector <edge> g[MAXN];

struct str{
	int u,v,P,ind;
	bool operator < (const str &t)const{return P < t.P;}
}seq[MAXN];

int dep[MAXN],dis[MAXN],par[MAXN][21];
int getlca (int u,int v){
	if (dep[u] < dep[v]) swap (u,v);
	for (Int i = 20,dis = dep[u] - dep[v];~i;-- i) if (dis >> i & 1) u = par[u][i];
	if (u == v) return u;
	else{
		for (Int i = 20;~i;-- i) if (par[u][i] ^ par[v][i]) u = par[u][i],v = par[v][i];
		return par[u][0];
	}
}

void dfs (int u,int fa){
	dep[u] = dep[fa] + 1,par[u][0] = fa;
	for (Int i = 1;i <= 20;++ i) par[u][i] = par[par[u][i - 1]][i - 1];
	for (edge to : g[u]){
		int v = to.v,w = to.w;
		if (v == fa) continue;
		dis[v] = dis[u] + w,dfs (v,u);
	}
}

int getjump (int u,int p){
	for (Int i = 20;~i;-- i) if (par[u][i] && dis[u] - dis[par[u][i]] <= p) 
//		cout << u << " -> " << par[u][i] << endl,
		p -= dis[u] - dis[par[u][i]],u = par[u][i];
	return u;
}

struct node{int lft,stp;};

node getit1 (int u,int lca,int p){
	int stp = 0;
	while (u ^ lca){
		int now = getjump (u,p);
		if (dep[now] > dep[lca]) stp ++,u = now;
		else break;
	}
	return node{dis[u] - dis[lca],stp};
}

int st[MAXN][21];
void makeit (int P){
	for (Int i = 1;i <= n;++ i) st[i][0] = getjump (i,P);
//	cout << P << ": ------------ " << endl;
	for (Int j = 1;(1 << j) <= n;++ j)
		for (Int i = 1;i <= n;++ i)
			st[i][j] = st[st[i][j - 1]][j - 1];
//			cout << i << " jump " << (1 << j) << ": " << st[i][j] << endl;
}

node getit2 (int u,int lca,int p){
	int stp = 0;
	for (Int i = 20;~i;-- i) if (st[u][i] && dep[st[u][i]] > dep[lca]) stp += (1 << i),u = st[u][i];
	return node{dis[u] - dis[lca],stp};
}

int solveit (int x,int y,int P){
	if (x == y) return 0;
	int lca = getlca (x,y);
	node t1 = P <= siz ? getit2 (x,lca,P) : getit1 (x,lca,P);
	node t2 = P <= siz ? getit2 (y,lca,P) : getit1 (y,lca,P);
	int now = t1.stp + t2.stp;
	if (t1.lft + t2.lft <= P) return now + 1;
	else return now + 2;
}

int ans[MAXN];
signed main(){
//	freopen ("data.in","r",stdin);
//	freopen ("f1.out","w",stdout);
	read (n),siz = sqrt (n);
	for (Int i = 2,u,v,w;i <= n;++ i) read (u,v,w),g[u].push_back (edge{v,w}),g[v].push_back (edge{u,w});
	dfs (1,0),read (m);
	for (Int i = 1;i <= m;++ i) read (seq[i].u,seq[i].v,seq[i].P),seq[i].ind = i;
	sort (seq + 1,seq + m + 1);
	for (Int i = 1;i <= m;++ i){
		if (seq[i].P <= siz){
			if (seq[i].P != seq[i - 1].P) makeit (seq[i].P);
			ans[seq[i].ind] = solveit (seq[i].u,seq[i].v,seq[i].P);
		}
		else ans[seq[i].ind] = solveit (seq[i].u,seq[i].v,seq[i].P);
	}
	for (Int i = 1;i <= m;++ i) write (ans[i]),putchar ('\n');
	return 0;
}
posted @ 2021-08-12 20:36  Dark_Romance  阅读(60)  评论(0编辑  收藏  举报