Portal
一棵n
个点的有根树,规定一种dfs
序,\(m\)次询问一个点\(u\)和一个区间\([l,r]\),求dfs序在这个区间内的叶子中,到\(u\)最小的距离。
n,m≤500000
这题在线直接搞很难搞, 考虑离线.
一开始想到就是按照区间来离线, 把询问挂在某个端点上. 但是没有什么可以利用的性质(比如说要求单调可以单调队列), 所以弃掉.
考虑按照询问点来离线,然后考虑一每条边的贡献, 若果进入了这条边, 那么到外面的距离会增加, 里面的部分会减少. 这样直接用线段树维护就可以了, 实现有点麻烦.
考虑这个问题的扩展, 如果要在线, 考虑主席树(参照CF893F), 对于求某一点到树上某一dfn区间的题目也可以这样离线做(参照BZOJ2159,Crash的文明世界),(HDU5449 RobotDog,还是有点像的).
离线题目就可以应用换根的思想, 利用已经计算的值快速计算其他值, 做树上题目一定要注意这点(*).
说那么多干什么, 不就是暴力么?
Code
#include
using namespace std;
#define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define clar(a, b) memset((a), (b), sizeof(a))
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define Debug(s) debug("The massage in line %d, Function %s: %s\n", __LINE__, __FUNCTION__, s)
typedef long long LL;
typedef long double LD;
int read() {
char ch = getchar();
int x = 0, flag = 1;
for(;!isdigit(ch); ch = getchar()) if(ch == '-') flag *= -1;
for(;isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
return x * flag;
}
void write(LL x) {
if(x < 0) putchar('-'), x = -x;
if(x >= 10) write(x / 10);
putchar(x % 10 + 48);
}
const int Maxn = 500009;
struct edge {
int to, nxt, w;
}g[Maxn << 2];
int n, q, head[Maxn], e;
LL dis[Maxn], isleaf[Maxn], ans[Maxn];
int size[Maxn];
void add(int u, int v, int w) {
g[++e] = (edge){v, head[u], w}, head[u] = e;
}
template struct SGMTtree {
LL tree[N << 3], add[N << 3];
#define lc(x) ((x) << 1)
#define rc(x) ((x) << 1 | 1)
#define ls rt << 1, l, mid
#define rs rt << 1 | 1, mid + 1, r
void pushup(int rt) { tree[rt] = min(tree[lc(rt)], tree[rc(rt)]); }
void pushdown(int rt) {
if (add[rt]) {
LL &v = add[rt];
tree[lc(rt)] += v; tree[rc(rt)] += v;
add[lc(rt)] += v; add[rc(rt)] += v;
v = 0;
}
}
void build(int rt, int l, int r) {
if (l == r) {
tree[rt] = isleaf[l] ? dis[l] : LLONG_MAX / 2;
return ;
}
int mid = (l + r) >> 1;
build(ls), build(rs);
pushup(rt);
}
void modify(int rt, int l, int r, int P, int Q, int v) {
if (P <= l && r <= Q) {
tree[rt] += v, add[rt] += v;
return ;
}
int mid = (l + r) >> 1; pushdown(rt);
if (Q <= mid) modify(ls, P, Q, v);
else if (P >= mid + 1) modify(rs, P, Q, v);
else modify(ls, P, Q, v), modify(rs, P, Q, v);
pushup(rt);
}
LL query(int rt, int l, int r, int P, int Q) {
if (P <= l && r <= Q) return tree[rt];
int mid = (l + r) >> 1; pushdown(rt);
if (Q <= mid) return query(ls, P, Q);
else if (P >= mid + 1) return query(rs, P, Q);
else return min(query(ls, P, Q), query(rs, P, Q));
}
#undef lc
#undef rc
#undef ls
#undef rs
};
SGMTtree SGT;
void dfsInit(int u, int pa) {
isleaf[u] = 1; size[u] = 1;
for (int i = head[u]; ~i; i = g[i].nxt) {
int v = g[i].to;
if (v != pa) {
dis[v] = dis[u] + g[i].w, isleaf[u] = 0;
dfsInit(v, u);
size[u] += size[v];
}
}
}
struct node {
int l, r, Id;
};
vector qset[Maxn];
void init() {
clar(head, -1);
n = read(), q = read();
rep (i, 2, n) {
int pa = read(), w = read();
add(pa, i, w), add(i, pa, w);
}
rep (i, 1, q) {
int u = read(), l = read(), r = read();
qset[u].push_back((node){l, r, i});
}
dfsInit(1, 0);
SGT.build(1, 1, n);
}
void dfsAnswer(int u, int pa) {
rep (i, 0, qset[u].size() - 1) {
node s = qset[u][i];
ans[s.Id] = SGT.query(1, 1, n, s.l, s.r);
}
for (int i = head[u]; ~i; i = g[i].nxt) {
int v = g[i].to;
if (v != pa) {
SGT.modify(1, 1, n, v, v + size[v] - 1, -g[i].w);
if (v != 1) SGT.modify(1, 1, n, 1, v - 1, g[i].w);
if (v + size[v] - 1 != n) SGT.modify(1, 1, n, v + size[v], n, g[i].w);
dfsAnswer(v, u);
SGT.modify(1, 1, n, v, v + size[v] - 1, g[i].w);
if (v != 1) SGT.modify(1, 1, n, 1, v - 1, -g[i].w);
if (v + size[v] - 1 != n) SGT.modify(1, 1, n, v + size[v], n, -g[i].w);
}
}
}
void solve() {
dfsAnswer(1, 0);
rep (i, 1, q) write(ans[i]), putchar('\n');
}
int main() {
freopen("CF1110F.in", "r", stdin);
freopen("CF1110F.out", "w", stdout);
init();
solve();
#ifdef Qrsikno
debug("\nRunning time: %.3lf(s)\n", clock() * 1.0 / CLOCKS_PER_SEC);
#endif
return 0;
}