题解 挖宝

传送门

MD 赛时写了一年只有 MLE 四十分
出来简单卡了一下空间就过了

正解是换根 DP
倍增处理 x 在路径上的情况
否则 xa,b 路径上第一个在 (a,b) 这条链上的点是可以知道的
那么离线换根 DP,对这个点子树内前 3 远的点,向上倍增即可

然而我选择了一个巨大难调写法
在路径上是容易的
否则分类讨论位置
令 lca 为 t
(1,t) 路径上可以直接倍增
否则可以找到xa,b 路径上第一个在 (a,b) 这条链上的点,令为 k
发现对于一个 i,可以用差分 dfs 求出其子树内不在某些儿子子树内的与其距离为 k 的点
具体地,dfs 时对每个深度维护要求这个深度的点的询问编号,进入某个被 ban 的子树时删掉,离开时加回去
然后发现当这个 x(a,b) 最近的点是 lca 而且它拐到了某个祖先的子树里就会变得尤其离谱
那么再离线一遍 dfs,DP 出每个点向上走,可以拐到祖先的其它儿子的子树里的最长链及在哪个祖先处取到
然后就转化为上面的问题了
挺好写的,也就 9.6k 而已
upd:原来这里直接长链剖分就可以避免一大车差分 dfs 了
复杂度 O(n\logn)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 1000010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, q;
int head[N], ecnt;
struct edge{int to, next;}e[N<<1];
inline void add(int s, int t) {e[++ecnt]={t, head[s]}; head[s]=ecnt;}

// namespace force{
// 	int dep[N], fa[21][N], back[N], lg[N];
// 	void dfs(int u, int pa) {
// 		for (int i=1; i<21; ++i)
// 			if (dep[u]>=1<<i) fa[i][u]=fa[i-1][fa[i-1][u]];
// 			else break;
// 		for (int i=head[u],v; ~i; i=e[i].next) {
// 			v = e[i].to;
// 			if (v==pa) continue;
// 			dep[v]=dep[u]+1;
// 			fa[0][v]=u;
// 			dfs(v, u);
// 		}
// 	}
// 	int lca(int a, int b) {
// 		if (dep[a]<dep[b]) swap(a, b);
// 		while (dep[a]>dep[b]) a=fa[lg[dep[a]-dep[b]]-1][a];
// 		if (a==b) return a;
// 		for (int i=lg[dep[a]]-1; ~i; --i)
// 			if (fa[i][a]!=fa[i][b])
// 				a=fa[i][a], b=fa[i][b];
// 		return fa[0][a];
// 	}
// 	int dis(int a, int b) {return dep[a]+dep[b]-2*dep[lca(a, b)];}

// 	// void isok(int a, int da, int b, int db, int x, int i) {
// 	// 	assert(x>=1&&x<=n);
// 	// 	// cout<<dis(a, x)<<' '<<da<<endl;
// 	// 	if (dis(a, x)!=da || dis(b, x)!=db) cerr<<"line: "<<i<<endl;
// 	// 	assert(dis(a, x)==da && dis(b, x)==db);
// 	// }

// 	// void isnone(int a, int da, int b, int db, int i) {
// 	// 	for (int x=1; x<=n; ++x) {
// 	// 		if (dis(a, x)==da && dis(b, x)==db) cerr<<"line: "<<i<<endl;
// 	// 		assert(dis(a, x)!=da || dis(b, x)!=db);
// 	// 	}
// 	// }

// 	void solve() {
// 		dep[1]=1; dfs(1, 0);
// 		for (int i=1; i<=n; ++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
// 		for (int i=1,a,b,da,db; i<=q; ++i) {
// 			// cout<<"i: "<<i<<endl;
// 			a=read(); da=read(); b=read(); db=read();
// 			for (int j=1; j<=n; ++j) if (dis(a, j)==da&&dis(b, j)==db) {
// 				printf("%d\n", j);
// 				goto jump;
// 			}
// 			puts("-1");
// 			jump: ;
// 		}
// 	}
// }

namespace task1{
	int _a[N], _b[N], _da[N], _db[N];
	int dep[N], mdep[N], back[N], mlen[N], fa[21][N], lg[N], ans[N];
	void dfs1(int u, int pa) {
		mdep[u]=dep[u];
		for (int i=1; i<21; ++i)
			if (dep[u]>=1<<i) fa[i][u]=fa[i-1][fa[i-1][u]];
			else break;
		for (int i=head[u],v; ~i; i=e[i].next) {
			v = e[i].to;
			if (v==pa) continue;
			dep[v]=dep[u]+1;
			fa[0][v]=u;
			back[v]=u;
			dfs1(v, u);
			mdep[u]=max(mdep[u], mdep[v]);
		}
		mlen[u]=mdep[u]-dep[u];
	}
	int lca(int a, int b) {
		if (dep[a]<dep[b]) swap(a, b);
		while (dep[a]>dep[b]) a=fa[lg[dep[a]-dep[b]]-1][a];
		if (a==b) return a;
		for (int i=lg[dep[a]]-1; ~i; --i)
			if (fa[i][a]!=fa[i][b])
				a=fa[i][a], b=fa[i][b];
		return fa[0][a];
	}
	int anc(int a, int k) {
		for (int i=lg[dep[a]]-1; ~i; --i)
			if (k>=1<<i) a=fa[i][a], k-=1<<i;
		return a;
	}
	int dis(int a, int b) {return dep[a]+dep[b]-2*dep[lca(a, b)];}
	namespace sub1{
		set<int> que[N];
		vector<pair<int, int>> buc[N], del[N], rec[N];
		void dfs2(int u, int fa) {
			for (auto it:del[u]) if (que[it.fir].find(it.sec)!=que[it.fir].end()) {
				que[it.fir].erase(it.sec);
				rec[u].pb(it);
			}
			for (auto it:buc[u]) que[it.fir].insert(it.sec);
			for (auto it:que[dep[u]]) ans[it]=u;
			que[dep[u]].clear();
			for (int i=head[u],v; ~i; i=e[i].next) {
				v = e[i].to;
				if (v==fa) continue;
				dfs2(v, u);
			}
			for (auto it:buc[u]) que[it.fir].erase(it.sec);
			for (auto it:rec[u]) que[it.fir].insert(it.sec);
		}
	}
	// namespace sub2{
	// 	set<int> que[N];
	// 	vector<pair<int, int>> buc[N], del[N], rec[N];
	// 	void dfs2(int u, int fa) {
	// 		for (auto it:del[u]) if (que[it.fir].find(it.sec)!=que[it.fir].end()) {
	// 			que[it.fir].erase(it.sec);
	// 			rec[u].pb(it);
	// 		}
	// 		for (auto it:buc[u]) que[it.fir].insert(it.sec);
	// 		for (auto it:que[dep[u]]) ans[it]=u;
	// 		que[dep[u]].clear();
	// 		for (int i=head[u],v; ~i; i=e[i].next) {
	// 			v = e[i].to;
	// 			if (v==fa) continue;
	// 			dfs2(v, u);
	// 		}
	// 		for (auto it:buc[u]) que[it.fir].erase(it.sec);
	// 		for (auto it:rec[u]) que[it.fir].insert(it.sec);
	// 	}
	// }
	// namespace sub3{
	// 	set<int> que[N];
	// 	vector<pair<int, int>> buc[N], del[N], rec[N];
	// 	void dfs2(int u, int fa) {
	// 		for (auto it:del[u]) if (que[it.fir].find(it.sec)!=que[it.fir].end()) {
	// 			que[it.fir].erase(it.sec);
	// 			rec[u].pb(it);
	// 		}
	// 		for (auto it:buc[u]) que[it.fir].insert(it.sec);
	// 		for (auto it:que[dep[u]]) ans[it]=u;
	// 		que[dep[u]].clear();
	// 		for (int i=head[u],v; ~i; i=e[i].next) {
	// 			v = e[i].to;
	// 			if (v==fa) continue;
	// 			dfs2(v, u);
	// 		}
	// 		for (auto it:buc[u]) que[it.fir].erase(it.sec);
	// 		for (auto it:rec[u]) que[it.fir].insert(it.sec);
	// 	}
	// }
	namespace sub4{
		set<int> que[N];
		vector<pair<int, int>> buc[N], del[N], rec[N];
		vector<pair<int, int>> tem[N];
		void dfs2(int u, int fa) {
			for (auto it:del[u]) if (que[it.fir].find(it.sec)!=que[it.fir].end()) {
				que[it.fir].erase(it.sec);
				rec[u].pb(it);
			}
			for (auto it:buc[u]) que[it.fir].insert(it.sec);
			for (auto it:que[dep[u]]) ans[it]=u;
			que[dep[u]].clear();
			for (int i=head[u],v; ~i; i=e[i].next) {
				v = e[i].to;
				if (v==fa) continue;
				dfs2(v, u);
			}
			for (auto it:buc[u]) que[it.fir].erase(it.sec);
			for (auto it:rec[u]) que[it.fir].insert(it.sec);
		}
		void dfs3(int u, int fa, int maxn, int pos, int dis) {
			for (auto it:tem[u]) {
				if (maxn>=it.fir) {
					buc[pos].pb({dep[pos]+it.fir-dis, it.sec});
					del[anc(u, dis-1)].pb({dep[pos]+it.fir-dis, it.sec});
				}
			}
			pair<int, int> fir, sec;
			for (int i=head[u],v; ~i; i=e[i].next) {
				v = e[i].to;
				if (v==fa) continue;
				if (mlen[v]+1>=fir.fir) sec=fir, fir={mlen[v]+1, v};
				else if (mlen[v]+1>sec.fir) sec={mlen[v]+1, v};
			}
			if (maxn>=fir.fir) sec=fir, fir={maxn, pos};
			else if (maxn>sec.fir) sec={maxn, pos};
			for (int i=head[u],v; ~i; i=e[i].next) {
				v = e[i].to;
				if (v==fa) continue;
				if (v==fir.sec) dfs3(v, u, sec.fir+1, sec.sec==pos?pos:u, sec.sec==pos?dis+1:1);
				else dfs3(v, u, fir.fir+1, fir.sec==pos?pos:u, fir.sec==pos?dis+1:1);
			}
		}
		void query(int t, int len, int id) {
			tem[t].pb({len, id});
		}
		void solve() {
			dfs3(1, 0, -INF, 0, 0);
			dfs2(1, 0);
		}
	}
	void solve() {
		// cout<<double(sizeof(sub4::buc)*16)/1000/1000<<endl; exit(0);
		// cout<<double(sizeof(lg)*31+sizeof(sub4::que)*4+sizeof(sub4::buc)*16)/1000/1000<<endl; exit(0);
		dep[1]=1; dfs1(1, 0);
		memset(ans, -1, sizeof(ans));
		for (int i=1; i<=n; ++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
		for (int i=1,a,b,da,db; i<=q; ++i) {
			// cout<<"i: "<<i<<endl;
			_a[i]=a=read(); _da[i]=da=read(); _b[i]=b=read(); _db[i]=db=read();
			if (!da||!db) {
				// cout<<"case1"<<endl;
				if (!da) ans[i]=a;
				else ans[i]=b;
				continue;
			}
			if (a==b) {
				if (da!=db) continue;
				if (da<dep[a]) ans[i]=anc(a, da);
				else sub1::buc[a].pb({dep[a]+da, i});
			}
			int t=lca(a, b), dis=dep[a]+dep[b]-2*dep[t], lena=dep[a]-dep[t], lenb=dep[b]-dep[t];
			// cout<<"t: "<<t<<endl;
			// cout<<"dis: "<<dis<<endl;
			if (da+db==dis) {
				// cout<<"case4"<<endl;
				if (da<=lena) ans[i]=anc(a, da);
				else assert(db<=lenb), ans[i]=anc(b, db);
			}
			else if (da+db>dis) {
				if (db-dis==da) {
					// cout<<"case2"<<endl;
					sub1::buc[a].pb({dep[a]+da, i});
					if (a==t) sub1::del[anc(b, lenb-1)].pb({dep[a]+da, i});
				}
				if (da-dis==db) {
					// cout<<"case3"<<endl;
					sub1::buc[b].pb({dep[b]+db, i});
					if (b==t) sub1::del[anc(a, lena-1)].pb({dep[b]+db, i});
				}
				if ((da+db-dis)&1) continue;
				int len=(da+db-dis)/2;
				// cout<<"dis: "<<dis<<endl;
				// cout<<"len: "<<len<<endl;
				if (lena+len==da && lenb+len==db) {
					if (len<dep[t]) ans[i]=anc(t, len);
					else {
						// --len;
						// for (int u=back[t],lst=t; u; lst=u,u=back[u],--len)
						// 	sub4::buc[u].pb({dep[u]+len, i}), sub4::del[lst].pb({dep[u]+len, i});
						// len=(da+db-dis)/2;
						sub4::query(t, len, i);
					}
				}

				// if (a!=t&&b!=t) { // in lca's subtree
				// 	sub2::buc[t].pb({dep[t]+len, i});
				// 	sub2::del[anc(a, lena-1)].pb({dep[t]+len, i});
				// 	sub2::del[anc(b, lenb-1)].pb({dep[t]+len, i});
				// }
				// if (lena>=2) sub3::query(a, t, len, i);
				// if (lenb>=2) sub3::query(b, t, len, i);

				if (a!=t && b!=t) { // in lca's subtree
					if (lena+len==da && lenb+len==db) {
						sub1::buc[t].pb({dep[t]+len, i});
						sub1::del[anc(a, lena-1)].pb({dep[t]+len, i});
						sub1::del[anc(b, lenb-1)].pb({dep[t]+len, i});
					}
				}
				if (lena>=2 && len<da && da-len<lena) {
					int u=anc(a, da-len), v=anc(a, da-len-1);
					sub1::buc[u].pb({dep[u]+len, i});
					sub1::del[v].pb({dep[u]+len, i});
				}
				if (lenb>=2 && len<db && db-len<lenb) {
					int u=anc(b, db-len), v=anc(b, db-len-1);
					sub1::buc[u].pb({dep[u]+len, i});
					sub1::del[v].pb({dep[u]+len, i});
				}
			}
		}
		sub1::dfs2(1, 0);
		// sub2::dfs2(1, 0);
		// sub3::dfs2(1, 0);
		sub4::solve();
		for (int i=1; i<=q; ++i) printf("%d\n", ans[i]);
	}
}

signed main()
{
	freopen("hunting.in", "r", stdin);
	freopen("hunting.out", "w", stdout);

	n=read(); q=read();
	memset(head, -1, sizeof(head));
	for (int i=1,u,v; i<n; ++i) {
		u=read(); v=read();
		add(u, v); add(v, u);
	}
	// if (n<=3000 && q<=3000) force::solve();
	// else task1::solve();
	task1::solve();

	return 0;
}
posted @   Administrator-09  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示