缩减二分值域,把 O(nlog2logv) 变成 O(nlog3n)。
用 zkw 线段树替换常规线段树。
可以记录下整个点分树的形态,这样就不用每次找根节点了。
在对单棵分治树进行 getdep 时,可以在 dep>R 后直接返回。
可以记录下每个分治树内部遍历的顺序,用栈记录,这样就递归次数的常数可以减少一半。
线段树只需记录 maxval,在查询时记录下其中一个端点,另一个端点可以最后再 DFS 一遍求得。
火车头可能有用。
快读快写可能有用。
具体卡常的细节可以看代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define vi vector<int>
#define eb emplace_back
#define pii pair<int, ll>
#define fi first
#define se second
#define TIME 1e3 * clock() / CLOCKS_PER_SEC
bool Mbe;
mt19937_64 rng(35);
constexpr int N = 1e5 + 10, inf = 1e9;
int n, m, L, R, res, rt, la, ra, RT;
int b[N], mx[N], sz[N], vis[N], rr[N << 1];
int head[N], cnt_e = 1;
struct edge {
int to, w, nxt;
} e[N << 1];
void adde(int u, int v, int w) {
++cnt_e, e[cnt_e].to = v, e[cnt_e].w = w, e[cnt_e].nxt = head[u], head[u] = cnt_e;
}
int val[(1 << 18) + 10], D;
void modify(int x, int v, int type) {
x += D;
if(type == 0) val[x] = v;
else val[x] = max(val[x], v);
for(int i = x >> 1; i; i >>= 1)
val[i] = max(val[i << 1], val[i << 1 | 1]);
}
int query(int l, int r) {
int res = -inf;
for(l += D - 1, r += D + 1; l ^ r ^ 1; l >>= 1, r >>= 1) {
if(~l & 1) res = max(res, val[l ^ 1]);
if(r & 1) res = max(res, val[r ^ 1]);
}
return res;
}
int mm;
int gv(int x) {
return (x >= mm) ? 1 : -1;
}
void findroot(int u, int ff, int num) {
sz[u] = 1, mx[u] = 0;
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(v == ff || vis[v]) continue;
findroot(v, u, num);
sz[u] += sz[v];
mx[u] = max(mx[u], sz[v]);
}
mx[u] = max(mx[u], num - sz[u]);
if(mx[u] < mx[rt]) rt = u;
}
int tp;
pii stk[N];
void calc(int u, int ff, int w, int dep) {
if(dep > R) return;
stk[++tp] = pii(dep, w);
if(dep < R && query(max(L - dep, 1), R - dep) + w >= 0) {
res = 1;
la = u;
return;
}
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(v == ff || vis[v]) continue;
calc(v, u, w + gv(e[i].w), dep + 1);
if(res) return;
}
}
void divide(int u) {
vis[u] = 1;
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(vis[v]) continue;
int tmp = tp;
calc(v, u, gv(e[i].w), 1);
if(res) {
while(tmp) modify(stk[tmp--].fi, -inf, 0);
tp = 0;
return;
}
for(int j = tmp + 1; j <= tp; ++j)
modify(stk[j].fi, stk[j].se, 1);
}
if(query(L, R) >= 0) {
res = 1;
la = u;
}
while(tp) modify(stk[tp--].fi, -inf, 0);
if(res) return;
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(vis[v]) continue;
divide(rr[i]);
}
}
bool check(int mid) {
mm = mid;
for(int i = 1; i <= n; ++i) vis[i] = 0;
res = 0;
divide(RT);
return res;
}
void getra(int u, int ff, int w, int dep) {
if(w >= 0 && ff && L <= dep && dep <= R) {
ra = u;
return;
}
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(v == ff) continue;
getra(v, u, w + gv(e[i].w), dep + 1);
if(ra > 0) return;
}
}
void init(int u) {
vis[u] = 1;
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(vis[v]) continue;
rt = 0;
findroot(v, u, sz[v]);
rr[i] = rt;
init(rr[i]);
}
}
bool Med;
int main() {
fprintf(stderr, "%.3lf MB\n", (&Mbe - &Med) / 1048576.0);
ios :: sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n >> L >> R;
D = 1 << (__lg(n + 1) + 1);
for(int i = 0; i < (1 << 18) + 10; ++i) val[i] = -inf;
int mxw = 0;
for(int i = 1; i < n; ++i) {
int u, v, w;
cin >> u >> v >> w;
b[i] = w;
mxw = max(mxw, w);
adde(u, v, w);
adde(v, u, w);
}
rt = 0;
mx[0] = N;
findroot(1, 0, n);
RT = rt;
init(RT);
sort(b + 1, b + n);
m = unique(b + 1, b + n) - b - 1;
for(int i = 1; i <= cnt_e; ++i) e[i].w = lower_bound(b + 1, b + m, e[i].w) - b;
int L = 1, R = m;
while(L <= R) {
int mid = (L + R) >> 1;
if(check(mid)) L = mid + 1;
else R = mid - 1;
}
check(R);
getra(la, 0, 0, 0);
cout << la << " " << ra << "\n";
cerr << TIME << "ms\n";
return 0;
}