「校内训练 2019-04-23」越野赛车问题 动态dp+树的直径
题目传送门
http://192.168.21.187/problem/1236
http://47.100.137.146/problem/1236
题解
题目中要求的显然是那个状态下的直径嘛。
所以这道题有一个非常简单的做法——线段树分治。
直接把每一条边按照 \(l, r\) 的区间放到线段树上进行分治,遍历的时候用并查集维护直径就可以了。
时间复杂度为 \(O(n\log^2n)\)。
很早以前就写了这个算法,代码附在了最后,不多讲了。
但是这道题还有一个方法——动态 DP。
线段树分治是给哪些不方便撤销不是按照栈的顺序撤销的操作的问题使用的,如果这个问题支持按照栈的顺序撤销修改,那么可以很好的使用线段树分治。
但是我们发现这个问题实际上不是栈的顺序的修改也可以做。也就是用动态 DP 维护树直径。
令 \(dp[x][0]\) 表示在 \(x\) 的子树子树中,从 \(x\) 上面那个边出发的最长链。(为什么是上面那个边:这样是否转移可以从 \(x\) 这里看出来,而不是需要在 \(x\) 的父亲那里转移的时候特判,方便 ddp)。
\(dp[x][1]\) 表示 \(x\) 的子树中的直径。
那么转移就是这样的(经典转移):
设树剖完以后,\(x\) 的重儿子为 \(son_x\),轻儿子集合为 \(light_x\)。令 \(h[x][0] = \max\limits_{y\in light_x} dp[y][0]\),\(h[x][1] = \max\{\max\limits_{y\in light_x} dp[y][1], \max\limits_{y_1,y_2\in light_x} dp[y_1][0] + dp[y_2][0]\}\)。
这样上面的 dp 式子可以改写成:
于是转移矩阵就是(定义矩乘为求和后取 \(\max\)):
然后 ddp 就可以了。
关于 \(h\) 的维护:
我们维护两个可删除堆,\(q1[x]\) 记录所有的轻儿子 \(y\) 的 \(dp[y][0]\),\(q2[x]\) 记录所有的轻儿子 \(y\) 的 \(dp[y][1]\)。
\(h[x][0]\):显然是取 \(q1[x]\) 里面最大的;
\(h[x][1]\):首先是 \(q2[x]\) 里面最大的,然后再看看 \(q1[x]\) 里面最大的两个数的和是不是更大。
时间复杂度还是 \(O(n\log^2 n)\)。貌似我写的这个 ddp 被线段树分治碾压,但是 thx 写的就碾压了线段树分治。(雾
#include<bits/stdc++.h>
#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back
template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}
typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;
template<typename I> inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
}
#define lc o << 1
#define rc o << 1 | 1
const int N = 70000 + 7;
const int INF = 0x3f3f3f3f;
int n, m, dfc;
int dep[N], f[N], siz[N], son[N], dfn[N], pre[N], bot[N], top[N];
int w[N], ans[N];
std::vector<int> v[N];
struct Edge { int to, ne, l, r; } g[N << 1]; int head[N], tot;
inline void addedge(int x, int y, int l, int r) { g[++tot].to = y, g[tot].l = l, g[tot].r = r, g[tot].ne = head[x], head[x] = tot; }
inline void adde(int x, int y, int l, int r) { addedge(x, y, l, r), addedge(y, x, l, r); }
struct Heap {
std::priority_queue<int> q, p;
inline void sync() { while (!q.empty() && !p.empty() && q.top() == p.top()) q.pop(), p.pop(); }
inline void push(int x) { q.push(x); sync(); }
inline void pop() { q.pop(); sync(); }
inline void del(int x) { p.push(x), sync(); }
inline int top() { sync(); return q.top(); }
inline bool empty() { sync(); return q.empty(); }
inline int size() { sync(); return q.size() - p.size(); }
} q1[N], q2[N];
inline int qtop(int x) {
if (q1[x].empty()) return 0;
else return q1[x].top();
}
inline int qtop2(int x) {
int ans = q2[x].empty() ? 0 : q2[x].top(), sum = 0, tmp;
if (q1[x].empty()) return ans;
sum = q1[x].top(), q1[x].pop(), tmp = sum;
if (!q1[x].empty()) sum += q1[x].top();
q1[x].push(tmp);
return std::max(sum, ans);
}// 记录一下这里的 sb 错误:最后返回的时候写成了 std::max(sum, tmp) 调了半个上午。
struct Matrix {
int a[3][3];
inline Matrix() { memset(a, 0, sizeof(a)); }
inline Matrix operator * (const Matrix &b) {
Matrix c;
c.a[0][0] = std::max(std::max(a[0][0] + b.a[0][0], a[0][1] + b.a[1][0]), a[0][2] + b.a[2][0]);
c.a[0][1] = std::max(std::max(a[0][0] + b.a[0][1], a[0][1] + b.a[1][1]), a[0][2] + b.a[2][1]);
c.a[0][2] = std::max(std::max(a[0][0] + b.a[0][2], a[0][1] + b.a[1][2]), a[0][2] + b.a[2][2]);
c.a[1][0] = std::max(std::max(a[1][0] + b.a[0][0], a[1][1] + b.a[1][0]), a[1][2] + b.a[2][0]);
c.a[1][1] = std::max(std::max(a[1][0] + b.a[0][1], a[1][1] + b.a[1][1]), a[1][2] + b.a[2][1]);
c.a[1][2] = std::max(std::max(a[1][0] + b.a[0][2], a[1][1] + b.a[1][2]), a[1][2] + b.a[2][2]);
c.a[2][0] = std::max(std::max(a[2][0] + b.a[0][0], a[2][1] + b.a[1][0]), a[2][2] + b.a[2][0]);
c.a[2][1] = std::max(std::max(a[2][0] + b.a[0][1], a[2][1] + b.a[1][1]), a[2][2] + b.a[2][1]);
c.a[2][2] = std::max(std::max(a[2][0] + b.a[0][2], a[2][1] + b.a[1][2]), a[2][2] + b.a[2][2]);
return c;
}
} t[N << 2];
inline void dfs1(int x, int fa = 0) {
dep[x] = dep[fa] + 1, f[x] = fa, siz[x] = 1;
for fec(i, x, y) if (y != fa) dfs1(y, x), siz[x] += siz[y], siz[y] > siz[son[x]] && (son[x] = y);
}
inline void dfs2(int x, int pa) {
top[x] = pa, dfn[x] = ++dfc, pre[dfc] = x;
if (!son[x]) return (void)(bot[x] = x);
dfs2(son[x], pa), bot[x] = bot[son[x]];
for fec(i, x, y) if (y != f[x] && y != son[x]) dfs2(y, y);
}
inline void build(int o, int L, int R) {
if (L == R) {
int x = pre[L];
if (w[x]) t[o].a[0][0] = 1, t[o].a[0][1] = -INF, t[o].a[0][2] = qtop(x) + 1;
else t[o].a[0][0] = -INF, t[o].a[0][1] = -INF, t[o].a[0][2] = 0;
t[o].a[1][0] = qtop(x), t[o].a[1][1] = 0, t[o].a[1][2] = qtop2(x);
t[o].a[2][0] = -INF, t[o].a[2][1] = -INF, t[o].a[2][2] = 0;
return;
}
int M = (L + R) >> 1;
build(lc, L, M), build(rc, M + 1, R);
t[o] = t[lc] * t[rc];
}
inline void qadd(int o, int L, int R, int x) {
if (L == R) {
int x = pre[L];
if (w[x]) t[o].a[0][0] = 1, t[o].a[0][1] = -INF, t[o].a[0][2] = qtop(x) + 1;
else t[o].a[0][0] = -INF, t[o].a[0][1] = -INF, t[o].a[0][2] = 0;
t[o].a[1][0] = qtop(x), t[o].a[1][1] = 0, t[o].a[1][2] = qtop2(x);
t[o].a[2][0] = -INF, t[o].a[2][1] = -INF, t[o].a[2][2] = 0;
return;
}
int M = (L + R) >> 1;
if (x <= M) qadd(lc, L, M, x);
else qadd(rc, M + 1, R, x);
t[o] = t[lc] * t[rc];
}
inline Matrix qsum(int o, int L, int R, int l, int r) {
if (l <= L && R <= r) return t[o];
int M = (L + R) >> 1;
if (r <= M) return qsum(lc, L, M, l, r);
if (l > M) return qsum(rc, M + 1, R, l, r);
return qsum(lc, L, M, l, r) * qsum(rc, M + 1, R, l, r);
}
inline Matrix qry(int x) { return qsum(1, 1, n, dfn[x], dfn[bot[x]]) * Matrix(); }
inline void upd(int x, int k) {
w[x] = k;
while (top[x] != 1) {
const Matrix &tmp1 = qry(top[x]);
qadd(1, 1, n, dfn[x]);
const Matrix &tmp2 = qry(top[x]);
x = f[top[x]];
q1[x].del(tmp1.a[0][0]), q1[x].push(tmp2.a[0][0]);
q2[x].del(tmp1.a[1][0]), q2[x].push(tmp2.a[1][0]);
}
qadd(1, 1, n, dfn[x]);
}
inline void work() {
dfs1(1), dfs2(1, 1), build(1, 1, n);
for (int x = 1; x <= n; ++x)
for fec(i, x, y) if (f[x] == y) v[g[i].l].pb(x), v[g[i].r].pb(-x);
for (int i = 1; i <= n; ++i) {
for (int x : v[i]) if (x > 0) upd(x, 1);
ans[i] = qry(1).a[1][0];
for (int x : v[i]) if (x < 0) upd(-x, 0);
}
int x;
while (m--) read(x), printf("%d\n", ans[x]);
}
inline void init() {
read(n), read(m);
int x, y, l, r;
for (int i = 1; i < n; ++i) read(x), read(y), read(l), read(r), adde(x, y, l, r);
}
int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#else
File(racing);
#endif
init();
work();
fclose(stdin), fclose(stdout);
return 0;
}
附录:线段树分治的做法以及每一个部分分的写法:
#include<bits/stdc++.h>
#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back
template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b , 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b , 1 : 0;}
typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;
template<typename I>
inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
}
const int N = 70000 + 7;
int n, m, flag_l1, flag_lian;
int _ask[N];
struct Edge {int to, ne, l, r;} g[N << 1]; int head[N], tot;
inline void addedge(int x, int y, int l, int r) {g[++tot].to = y; g[tot].l = l; g[tot].r = r; g[tot].ne = head[x]; head[x] = tot;}
inline void adde(int x, int y, int l, int r) {addedge(x, y, l, r); addedge(y, x, l, r);}
namespace Task1 {
int v, ans;
inline void dfs(int x, int dep = 0, int fa = 0) {
smax(ans, dep);
for fec(i, x, y) if (y != fa) {
if (v < g[i].l || v > g[i].r) continue;
dfs(y, dep + 1, x);
}
}
inline void work() {
for (int i = 1; i <= m; ++i) {
v = _ask[i]; ans = 0;
for (int j = 1; j <= n; ++j) dfs(j);
printf("%d\n", ans);
}
}
}
namespace Task2 {
int ans[N], w[N];
std::set<pii> s;
std::vector<int> wph[N];
inline void work() {
for (int i = 1; i < n; ++i)
for fec(j, i, y) if (y == i + 1) w[i] = g[j].r;
for (int i = 1; i < n; ++i) wph[w[i]].pb(i);
for (int i = 1; i <= n; ++i) s.insert(pii(i, i));
for (int i = n; i; --i) {
ans[i] = ans[i + 1];
if (wph[i].empty()) continue;
for (auto j : wph[i]) {
auto p = s.upper_bound(pii(j, n)), q = p;
--q;
smax(ans[i], p->se - q->fi);
pii neww(q->fi, p->se);
s.erase(p), s.erase(q);
s.insert(neww);
}
}
for (int i = 1; i <= m; ++i) printf("%d\n", ans[_ask[i]]);
}
}
namespace Task3 {
int ans[N], w[N], v[N];
std::set<pii> s;
std::vector<int> wph[N], thx[N];
struct Heap {
std::priority_queue<int> q, p;
inline void sync() {while (!q.empty() && !p.empty() && q.top() == p.top()) q.pop(), p.pop();}
inline void push(int x) {q.push(x), sync();}
inline void del(int x) {p.push(x), sync();}
inline int top() {sync(); return q.top();}
inline bool empty() {sync(); return q.empty();}
} yql;
inline void work() {
for (int i = 1; i < n; ++i)
for fec(j, i, y) if (y == i + 1) w[i] = g[j].r, v[i] = g[j].l;
for (int i = 1; i < n; ++i) wph[w[i]].pb(i);
for (int i = 1; i < n; ++i) thx[v[i]].pb(i);
for (int i = 1; i <= n; ++i) s.insert(pii(i, i)), yql.push(0);
for (int i = n; i; --i) {
for (auto j : wph[i]) {
auto p = s.upper_bound(pii(j, n)), q = p;
--q;
pii neww(q->fi, p->se);
yql.del(q->se - q->fi), yql.del(p->se - p->fi);
s.erase(p), s.erase(q);
s.insert(neww);
yql.push(neww.se - neww.fi);
}
ans[i] = yql.top();
// dbg("i = %d, ans[i] = %d\n", i, ans[i]);
for (auto j : thx[i]) {
// dbg("-----orz: ----- i = %d, j = %d\n", i, j);
auto p = s.upper_bound(pii(j, n));
--p;
pii new1(p->fi, j), new2(j + 1, p->se);
yql.del(p->se - p->fi);
s.erase(p);
yql.push(new1.se - new1.fi), yql.push(new2.se - new2.fi);
s.insert(new1), s.insert(new2);
}
}
for (int i = 1; i <= m; ++i) printf("%d\n", ans[_ask[i]]);
}
}
namespace Task4 {
#define lc o << 1
#define rc o << 1 | 1
inline int dist(int, int);
int dfc;
int dfn[N], dep[N], siz[N], son[N], f[N], top[N];
int fa[N], mxd[N], ans[N];
struct wph {
int x, y, len;
inline wph() {len = 0;}
inline wph(int x, int y) : x(x), y(y) {len = dist(x, y);}
inline bool operator < (const wph &a) const {return len < a.len;}
} val[N];
inline wph merge(wph, wph);
inline int find(int x) {return fa[x] == x ? x : find(fa[x]);}
inline void merge(int x, int y) {
x = find(x), y = find(y);
if (mxd[x] < mxd[y]) std::swap(x, y);
fa[y] = x; val[x] = merge(val[x], val[y]);
smax(mxd[x], mxd[y] + 1);
}
inline void dfs1(int x, int fa = 0) {
dep[x] = dep[fa] + 1; f[x] = fa; siz[x] = 1;
for fec(i, x, y) if (y != fa) dfs1(y, x), siz[x] += siz[y], siz[y] > siz[son[x]] && (son[x] = y);
}
inline void dfs2(int x, int pa) {
top[x] = pa; dfn[x] = ++dfc;
if (!son[x]) return; dfs2(son[x], pa);
for fec(i, x, y) if (y != f[x] && y != son[x]) dfs2(y, y);
}
inline int LCA(int x, int y) {
while (top[x] != top[y]) dep[top[x]] > dep[top[y]] ? x = f[top[x]] : y = f[top[y]];
return dep[x] < dep[y] ? x : y;
}
inline int dist(int x, int y) {return dep[x] + dep[y] - (dep[LCA(x, y)] << 1);}
std::vector<pii> t[N << 2], rem[N << 2];
std::vector<wph> remx[N << 2], remy[N << 2];
inline void qadd(int o, int L, int R, int l, int r, pii k) {
if (l <= L && R <= r) return t[o].pb(k);
int M = (L + R) >> 1;
if (l <= M) qadd(lc, L, M, l, r, k);
if (r > M) qadd(rc, M + 1, R, l, r, k);
}
inline wph merge(wph a, wph b) {
wph ans = std::max(a, b);
smax(ans, wph(a.x, b.x));
smax(ans, wph(a.x, b.y));
smax(ans, wph(a.y, b.x));
smax(ans, wph(a.y, b.y));
return ans;
}
inline void build() {
for (int i = 1; i <= tot; i += 2) {
int x = g[i].to, y = g[i + 1].to, l = g[i].l, r = g[i].r;
qadd(1, 1, n, l, r, pii(x, y));
}
}
inline void solve(int o, int L, int R, int ansv) {
int len = t[o].size();
for (auto i : t[o]) {
int x = i.fi, y = i.se;
rem[o].pb(pii(x = find(x), y = find(y)));
remx[o].pb(val[x]), remy[o].pb(val[y]);
merge(x, y); smax(ansv, std::max(val[x].len, val[y].len));
}
if (L == R) ans[L] = ansv;
else {
int M = (L + R) >> 1;
solve(lc, L, M, ansv), solve(rc, M + 1, R, ansv);
}
for (int ii = len - 1; ~ii; --ii) {
auto i = t[o][ii];
int x = i.fi, y = i.se;
x = rem[o][ii].fi, y = rem[o][ii].se; rem[o].pop_back();
fa[x] = x, fa[y] = y;
val[x] = remx[o][ii], val[y] = remy[o][ii];
remx[o].pop_back(), remy[o].pop_back();
}
}
inline void ycl() {
for (int i = 1; i <= n; ++i) {
fa[i] = i;
val[i] = wph(i, i);
}
}
inline void work() {
build();
dfs1(1), dfs2(1, 1);
ycl();
solve(1, 1, n, 0);
for (int i = 1; i <= m; ++i) printf("%d\n", ans[_ask[i]]);
}
}
inline void init() {
int u, v, l, r;
read(n), read(m);
flag_l1 = flag_lian = 1;
for (int i = 1; i < n; ++i) {
read(u), read(v), read(l), read(r);
adde(u, v, l, r);
flag_l1 = flag_l1 && l == 1;
flag_lian = flag_lian && u == i && v == i + 1;
}
for (int i = 1; i <= m; ++i) read(_ask[i]);
}
int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#else
File(racing);
#endif
init();
if (flag_l1 && flag_lian) Task2::work();
else if (flag_lian) Task3::work();
else if (n <= 20 && m <= 20) Task1::work();
else Task4::work();
fclose(stdin), fclose(stdout);
return 0;
}