Red Black Tree
磨磨蹭蹭地写虚树,结果半天没出来。大佬说 二分 求公共节点的lca,一下就出来了
二分就是取那些要变更的点的lca 然后判断这样log^2,好像也可以排序差分区间和弄到log,虚树就是暴力枚举然后换根dp,没弄好debug一下午。
虚树
// 基本没啥用就 两个函数是要想的,其他都是虚树构造必须虚树构造相关操作
void dfs(int u, int f);
int lca(int u, int v);
LL build(int n);需要思考的操作
LL dfs(int u); // 处理 ma ,K 和 L;
LL df(int u, LL k); int h[N], ne[N<<1], e[N<<1], w[N<<1], idx;
void add(int a, int b, int c){e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;return ;}int dfn[N], cv, fa[N][20], deep[N];
LL mi[N], ma[N], K[N], L[N], de[N];
// mi[i] i点的值, ma[i] i为根的子树的值的最大值;
// K[i] 以i为根的子树 到i的路径上有红色点的值的最大值,L[i] 以i为根的子树 到 i的路径上没有红色点的值的最大值
// de[i] i 到 整棵树的根节点的路径和, 用来判断 两点之间是否有红色点;
bool c[N]; // = 1 代表是红色点 void dfs(int u, int f) // 预处理 mi lca
{dfn[u] = cv ++; deep[u] = deep[f] + 1;for(int i = 1;i < 20;i ++) fa[u][i] = fa[fa[u][i-1]][i-1];for(int i = h[u];~i ;i = ne[i]){if(e[i] == f)continue;fa[e[i]][0] = u; mi[e[i]] = c[e[i]] ? 0 : mi[u] + w[i]; de[e[i]] = de[u] + w[i];dfs(e[i], u);}
}int lca(int u, int v)
{if(deep[u]<deep[v])swap(u, v);int x = deep[u] - deep[v];for(int i = 0;i < 20;i ++)if(x>>i&1)u = fa[u][i];if(u == v)return v;for(int i = 19;i >= 0;i --)if(fa[u][i] != fa[v][i])u = fa[u][i], v = fa[v][i];return fa[u][0];
}
int a[N];
bool dis[N];
bool cmp(int a,int b){return dfn[a]<dfn[b];}bool in(int u, int v){return !c[u] && mi[v] - mi[u] == de[v] - de[u];} // 判断 u -> v 是否有红色节点。
LL dfs(int u) // 处理 ma ,K 和 L;
{ma[u] = K[u] = L[u] = 0;if(dis[u]) ma[u] = mi[u], (c[u] ? K[u] = mi[u] : L[u] = mi[u]); for(int i = h[u]; ~i;i = ne[i]){ma[u] = max(ma[u], dfs(e[i])); K[u] = max(K[u], K[e[i]]);in(u, e[i]) ? L[u] = max(L[u], L[e[i]]) : K[u] = max(K[u], L[e[i]]);}return ma[u];
}// 处理答案 k 是 以u为根的树之外的 节点的值 的 最大值;
// 那么 把 u 作为 红色节点的答案 = max(k, max(K[u], L[u]-mi[i]));
LL df(int u, LL k)
{vector<LL> v, m; for(int i = h[u]; ~i;i = ne[i])v.push_back(ma[e[i]]), m.push_back(ma[e[i]]);for(int i = 1;i < v.size();i ++) v[i] = max(v[i], v[i-1]);for(int i = m.size()-2;i >= 0;i --) m[i] = max(m[i], m[i+1]); // 处理这棵树的前缀最大值和后缀最大值。int now = 0;LL ans = max(k, max(K[u], L[u]-mi[u]));for(int i = h[u]; ~i;i = ne[i]){LL tem = k;if(dis[u]) tem = max(tem, mi[u]);if(now != v.size()-1) tem = max(tem, m[now+1]);if(now) tem = max(tem, v[now-1]); ans = min(ans, df(e[i], tem)); ++ now;}h[u] = -1;return ans;
}
stack<int> st;
LL build(int n) // 建立虚树
{st.push(1); idx = 0;int tmp;for(int i = 1;i <= n;i ++){dis[a[i]] = 1;if(a[i] == 1) continue;int lc = lca(a[i], st.top());while(lc != st.top()){tmp = st.top(); st.pop();if(dfn[st.top()] < dfn[lc]) st.push(lc);add(st.top(), tmp, 0);}st.push(a[i]);}while(st.size() > 1){tmp = st.top(); st.pop();add(st.top(), tmp, 0);}st.pop();dfs(1);return df(1, 0);
}int main()
{int t;scanf("%d", &t);while(t --){int n, m, q;scanf("%d%d%d", &n, &m, &q);for(int i = 1;i <= n;i ++) h[i] = -1; idx = 0; cv = 0;for(int i = 1;i <= m;i ++){int x;scanf("%d", &x);c[x] = 1;}for(int i = 2;i <= n;i ++){int u, v, w;scanf("%d%d%d", &u, &v, &w);add(u, v, w); add(v, u, w);}dfs(1, 0);for(int i = 1;i <= n;i ++) h[i] = -1;for(int _ = 1;_ <= q;_ ++){int k;scanf("%d", &k);for(int i = 1;i <= k;i ++) scanf("%d", a+i);sort(a+1, a+k+1, cmp);printf("%lld\n", build(k));for(int i = 1;i <= k;i ++) dis[a[i]] = 0;}for(int i = 1;i <= n;i ++) c[i] = 0;}return 0;
}
二分
int h[N], ne[N<<1], e[N<<1], w[N<<1], idx;
void add(int a, int b, int c){e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;return ;}int fa[N][20], deep[N], a[N];
LL mi[N], de[N];
bool c[N];void dfs(int u, int f)
{deep[u] = deep[f] + 1;for(int i = 1;i < 20;i ++) fa[u][i] = fa[fa[u][i-1]][i-1];for(int i = h[u];~i ;i = ne[i]){if(e[i] == f)continue;fa[e[i]][0] = u; mi[e[i]] = c[e[i]] ? 0 : mi[u] + w[i]; de[e[i]] = de[u] + w[i];dfs(e[i], u);}
}
int lca(int u, int v)
{if(deep[u]<deep[v])swap(u, v);int x = deep[u] - deep[v];for(int i = 0;i < 20;i ++)if(x>>i&1)u = fa[u][i];if(u == v)return v;for(int i = 19;i >= 0;i --)if(fa[u][i] != fa[v][i])u = fa[u][i], v = fa[v][i];return fa[u][0];
}bool cmp(int u, int v){return mi[u] < mi[v];}
bool in(int u, int v){return !c[u] && mi[v]-mi[u] == de[v] - de[u];} // 判断 u -> v 是否有红色节点。
bool ch(LL t, int n)
{int now = 0;for(int i = 1;i <= n;i ++)if(mi[a[i]] > t) now = now ? lca(now, a[i]) : a[i];for(int i = 1;i <= n;i ++)if(mi[a[i]] > t)if(!in(now, a[i]) || mi[a[i]] - mi[now] > t)return 0;return 1;
}int main()
{int t;scanf("%d", &t);while(t --){int n, m, q;scanf("%d%d%d", &n, &m, &q);for(int i = 1;i <= n;i ++) h[i] = -1; idx = 0; for(int i = 1;i <= m;i ++){int x;scanf("%d", &x);c[x] = 1;}for(int i = 2;i <= n;i ++){int u, v, w;scanf("%d%d%d", &u, &v, &w);add(u, v, w); add(v, u, w);}dfs(1, 0);for(int _ = 1;_ <= q;_ ++){int k;scanf("%d", &k);for(int i = 1;i <= k;i ++) scanf("%d", a+i);sort(a+1, a+k+1, cmp);LL l = 0, r = 1e17;while(l < r)ch(mid, k) ? r = mid : l = mid+1;printf("%lld\n", l);}for(int i = 1;i <= n;i ++) c[i] = 0;}return 0;
}