Nauuo and Binary Tree(交互)

传送门
让你询问\(?\ u\ v\),反馈你两点间树上距离,让你在\(nlogn\)次询问内,能够得出树的结构。
先询问所有点到\(1\)的距离(\(dep\)),将所有点按\(dep\)排序,这样依次加入每个点找父亲。已知该点在\(u\)子树中,令\(ed[u]\)\(u\)所在重链的链尾。询问该点到\(ed[u]\)距离,可推出两点的lca深度,可以从\(ed[u]\)往上跳到lca处,然后可确定\(u\)在lca的轻儿子所在子树内部(子问题)。
每一次询问都对应往下走一次轻边,而一个点往上走的轻边数量为\(logn\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int n, dep[N], A[N], fa[N], lgt[N], hvy[N], ed[N], sz[N];
bool cmp(int u, int v) {return dep[u] < dep[v];}
void New_son(int x) {
//	printf("HEV: %d\n", x);
	int u = x;
	while(u != 1) {
		int f = fa[u];
		sz[f]++;
		if(sz[lgt[f]] > sz[hvy[f]]) {
			swap(lgt[f], hvy[f]);
		}
		ed[f] = ed[hvy[f]];
		u = f;
	}
}
void solve(int u, int nd) {
//	printf("Now u=%d nd=%d\n", u, nd);
	printf("? %d %d\n", nd, ed[u]); fflush(stdout);
	int dis; scanf("%d", &dis);
	int depc = (dep[nd] + dep[ed[u]] - dis) >> 1;
	int cur = ed[u];
	while(dep[cur] > depc) cur = fa[cur];
//	printf("depc=%d cur=%d\n",depc, cur);
	if(!hvy[cur]) {fa[nd] = cur; hvy[cur] = nd;}
	else if(!lgt[cur]) {fa[nd] = cur; lgt[cur] = nd;}
	else solve(lgt[cur], nd);
}
int main() {
	scanf("%d", &n);
	for(int i = 2; i <= n; i++) {
		printf("? 1 %d\n", i); fflush(stdout);
		scanf("%d", &dep[i]);
		A[i] = i;
	}
	sz[ed[1] = 1] = 1;
	sort(A + 2, A + 1 + n, cmp);
	for(int i = 2; i <= n; i++) {
		int x = A[i]; ed[x] = x; sz[x] = 1;
//		printf("*");
		solve(1, x);
		New_son(x);
	}
	printf("!"); for(int i = 2; i <= n; i++) {printf(" %d", fa[i]);}
	puts(""); fflush(stdout);
	return 0;
}

「GXOI / GZOI2019」旧词

传送门
之前做过一道叫:LCN的题好像也用过这个套路:查询一个点与多个点的dep[lca]和,可以先预先将那些点到根的路径值+1,查询的时候也到根的路径求和即可,因为重叠部分(即lca往上)全会被计入。
这道题因为在线,需要树剖+线段树。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
const int M = N << 2;
const int mod = 998244353;
ll ksm(ll a, ll b) {ll mul(1); for(; b; b >>= 1, a = a * a % mod) if(b & 1)mul = mul * a % mod; return mul;}
ll pw[N];
int dep[N], rt[N], fa[N], head[N], ecnt, nxt[N], to[N], sz[N], son[N], top[N], Id[N], In[N], Time, Out[N], ed[N];
struct query {int x, y, id;} Q[N];
bool cmp(query u, query v) {return u.x < v.x;}
void add_edge(int u, int v) {nxt[++ecnt] = head[u]; to[ecnt] = v; head[u] = ecnt;}

void gt_son(int u) {
	sz[u] = 1; dep[u] = dep[fa[u]] + 1;
	for(int i = head[u]; i; i = nxt[i]) {
		int v = to[i];
		gt_son(v);
		sz[u] += sz[v];
		if(sz[v] > sz[son[u]]) {son[u] = v;}
	}
}
void gt_top(int u, int Tp) {
	top[u] = Tp; In[u] = ++Time; Id[Time] = u;
	if(son[u]) {
		gt_top(son[u], Tp);
		ed[u] = ed[son[u]];
	} 
	else ed[u] = u;
	for(int i = head[u]; i; i = nxt[i]) {
		int v = to[i];
		if(v == son[u]) continue;
		gt_top(v, v);
	}
	Out[u] = Time;
}
int ls[M], rs[M], nd;
ll val[M], sum[M], tag[M];
void P_up(int x) {
	sum[x] = (sum[ls[x]] + sum[rs[x]]) % mod;
}
void Add_tag(int x, int w) {sum[x] = (sum[x] + w * val[x]) % mod; tag[x] = (tag[x] + w) % mod;}
void P_dw(int x) {
	if(!tag[x]) return;
	Add_tag(ls[x], tag[x]), Add_tag(rs[x], tag[x]);
	tag[x] = 0;
}
void Build(int &x, int l, int r) {
	x = ++nd;
	if(l == r) {val[x] = pw[dep[Id[l]]]; return;}
	int mid = (l + r) >> 1;
	Build(ls[x], l, mid), Build(rs[x], mid + 1, r);
	val[x] = (val[ls[x]] + val[rs[x]]) % mod;
//	printf("val[%d,%d] = %lld\n", l, r, val[x]);
}
void Update(int x, int l, int r, int p, int q) {
	if(p <= l && r <= q) {Add_tag(x, 1);return;}
	P_dw(x);
	int mid = (l + r) >> 1;
	if(p <= mid) Update(ls[x], l, mid, p, q);
	if(q > mid) Update(rs[x], mid + 1, r, p, q);
	P_up(x);
}
ll Ask(int x, int l, int r, int p, int q) {
	if(p <= l && r <= q) {return sum[x];}
	P_dw(x);
	int mid = (l + r) >> 1; ll res = 0;
	if(p <= mid) res = Ask(ls[x], l, mid, p, q);
	if(q > mid) res = (res + Ask(rs[x], mid + 1, r, p, q)) % mod;
	return res;
}

void Change(int x) {
	while(x) {
		int tp = top[x];
//		printf("Chg x=%d: tp=%d [%d,%d]\n", x, tp, In[tp], In[x]);
		Update(rt[tp], In[tp], In[ed[x]], In[tp], In[x]);
		x = fa[tp];
	}
}

ll Query(int x) {
	ll ans = 0;
	while(x) {
		int tp = top[x];
//		printf("Qry x=%d: tp=%d [%d,%d]\n", x, tp, In[tp], In[x]);
		ans = (ans + Ask(rt[tp], In[tp], In[ed[x]], In[tp], In[x])) % mod;
		x = fa[tp];
	}
	return ans;
}
ll res[N];
int main() {
	int n, q, k;
	scanf("%d%d%d", &n, &q, &k);
	for(int i = 1; i <= n; i++) {pw[i] = ksm(i, k);}
	for(int i = n; i; i--) pw[i] -= pw[i - 1];
	for(int i = 2; i <= n; i++) scanf("%d", &fa[i]), add_edge(fa[i], i);
	for(int i = 1; i <= q; i++) {
		scanf("%d%d", &Q[i].x, &Q[i].y); Q[i].id = i;
	}
	sort(Q + 1, Q + 1 + q, cmp);
	gt_son(1); gt_top(1, 1);
	for(int l = 1, r; l <= n; l = r + 1) {
		int x = Id[l];
		r = In[ed[x]];
		Build(rt[x], l, r);
	}
	for(int i = 1, j = 0; i <= q; i++) {
		while(j < Q[i].x) {Change(++j);}
		res[Q[i].id] = Query(Q[i].y);
	}
	for(int i = 1; i <= q; i++) {printf("%lld\n", (res[i] + mod) % mod);}
	return 0;
}