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;
}