学习笔记:虚树
有没有种可能,马上就要上考场了,不应该学新的东西(?)
算了,not important。
《不 应 该 学 新 的 东 西》
是谁的模拟赛有动态虚树?
板子
在某些情况下,我们要考虑的只有关键节点和他们的lca们组成的树。
实现:
先dfs,记录dfn和dep。然后把关键点按照dfn排序一个一个加入。
开一个栈,存的是准备塞进虚树但是还没有塞的(虚树上从根到stack[top]的路径上的点)。
假如准备加入新关键点v,那么分类讨论
- lca(stack[top],v)=stack[top] v直接进栈
- lca(stack[top],v)!=stack[top]
说明stack[top]在虚树上的子树已经全部被遍历过,可以拿去构建虚树,退出他们并加进虚树。
之后v进栈
void dfs(int u, int fa) {
dfn[u] = ++tim; dep[u] = dep[fa] + 1; mxdep = max(mxdep, dep[u]);
vec[dep[u]].push_back(u);
for(int i = hd[u]; i; i = nxt[i]) {
int v = to[i]; if(v == fa) continue;
f[v][0] = u;
for(int j = 1; j <= lim; j++)
f[v][j] = f[f[v][j - 1]][j - 1];
dfs(v, u);
}
}
ll solve() {
top = 0;
stk[++top] = 1; G[1].clear();
for(int i = 0; i < vec[d].size(); i++) { //这是Color the tree的代码。
int v = vec[d][i], lca = getlca(stk[top], v);
G[v].clear();
if(lca == stk[top]) stk[++top] = v; //不要写错
else {
while(top && dfn[stk[top - 1]] >= dfn[lca]) {//不要写错
G[stk[top - 1]].push_back(stk[top]);
top--;
}
if(lca != stk[top]) {
G[lca].clear();
G[lca].push_back(stk[top]);
stk[top] = lca;
}
stk[++top] = v;
}
}
while(top > 1) {
G[stk[top - 1]].push_back(stk[top]);
top--;
}
return DP(1, 0);
}
题睦
Color the Tree
我写ucup被虚树打爆了,所以这是我的虚树入门题!!1
题意:
你有一棵全白的树。
有n种操作。第i种操作能以 \(a_i\) 的代价涂黑距【你在这次操作时选择的点u】为i且是u的子孙节点的点v们。每种操作可以用多次。
题解:
实际上不同深度的点相互之间并不影响,那么我们分开考虑每一层的点。
考虑处理深度为 \(d\) 的层:
设 \(dp_u\) 为涂黑u子树内在主树上深度为d的点的最小代价
\[dp_u=min(a_{d-{dep}_u},\sum_{v\in son(u)} dp_v)
\]
这是 \(O(n^2)\) 的,T飞啦。
实际上我们要考虑的只有叶子节点和他们的lca们组成的树,你们这是个什么树阿,你们这原来是个虚树。
Warn:总该不会有人学了五年OI把0开始下标当成1开始下标,WA on 2然后心态爆炸几小时吧?
Kingdom and its Cities
你发现 \(\sum k\) 很小
你掏出一个虚树板子。
这是你的dp部分,你懒得解释。
你由于虚树两个地方写错,dp写错挂飞了。
PII DP(int u, int fa) { //?
//cout<<"DP: "<<u<<" "<<fa<<endl;
int ret = 0, qwq = 0;
for(int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
PII tmp = DP(v, u);
ret += tmp.se;
qwq += (tmp.fi == 1);
}
if(zy[u]) {
ret += qwq;
return mkp(1, ret);
} else {
if(qwq == 0) return mkp(0, ret); //有的人就是直接忽略这种情况。啸了
else if(qwq == 1)
return mkp(1, ret);
else return mkp(0, ret + 1);
}
}
P3233 [HNOI2014]世界树
代码实现有点烦,但小样例很强,一遍过了
看到 $\sum $ 那么我们知道它是虚树,但是,这 并 不 是 难 点
- 考虑虚树上某个点究竟被谁占领,只要 \(O(n)\) dfs两次计算各个点到哪个议事处最近就好了
- 考虑非虚数上的点,也就是藏在边上的(为了实现方便我们把1加进虚树,让每个点都在虚树边上/虚树边上的点或端点相连的被隐去的子树)。
- 考虑虚树边的两端:买虚树上的点送它连着的不在虚树上的子树。
- 考虑虚树边上子树的点/虚树边上的点:
如果颜色相同:这些点都是该颜色的
如果颜色不同:中间一定有个分界点可以分开两种颜色的领地
倍增维护一下就好。《一 下 就 好》
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mkp make_pair
#define ls(x) ((x)<<1)
#define rs(x) ((x)<<1|1)
#define fi first
#define se second
#define PII pair<int,int>
const int N = 3e5 + 10, lim = 19, inf = 0x3f3f3f3f;
int top, n, tim, dfn[N], dep[N], f[N][25], sz[N];
int k, e, to[N << 1], nxt[N << 1], hd[N], d, stk[N];
int gj[N], res[N], isgj[N];
PII dist[N];
vector<int>G[N];
void add(int u, int v) { to[++e] = v; nxt[e] = hd[u]; hd[u] = e; }
void dfs1(int u, int fa) {
sz[u] = 1;
dfn[u] = ++tim; dep[u] = dep[fa] + 1;
for(int i = hd[u]; i; i = nxt[i]) {
int v = to[i]; if(v == fa) continue;
f[v][0] = u;
for(int j = 1; j <= lim; j++)
f[v][j] = f[f[v][j - 1]][j - 1];
dfs1(v, u);
sz[u] += sz[v];
}
}
int getlca(int x, int y) {
if(dep[x] < dep[y]) swap(x, y);
for(int i = lim; i >= 0; i--)
if(dep[f[x][i]] >= dep[y])
x = f[x][i];
if(x == y) return x;
for(int i = lim; i >= 0; i--)
if(f[x][i] != f[y][i])
x = f[x][i], y = f[y][i];
return f[x][0];
}
int getdis(int x, int y) {
int t = getlca(x, y);
return dep[x] + dep[y] - 2 * dep[t];
}
void build() {
top = 0;
stk[++top] = 1; G[1].clear();
for(int i = 1; i <= k; i++) {
int v = gj[i]; G[v].clear();
if(v == 1) continue;
int lca = getlca(stk[top], v);
if(lca == stk[top]) stk[++top] = v;
else {
while(top > 1 && dep[stk[top - 1]] >= dep[lca]) {
G[stk[top - 1]].push_back(stk[top]);
// cout<<stk[top - 1]<<" "<<stk[top]<<endl;
top--;
}
if(stk[top] != lca) {
G[lca].clear();
G[lca].push_back(stk[top]);
// cout<<lca<<" "<<stk[top]<<endl;
stk[top] = lca;
}
stk[++top] = v;
}
}
while(top > 1) {
G[stk[top - 1]].push_back(stk[top]);
//cout<<stk[top - 1]<<" "<<stk[top]<<endl;
top--;
}
return;
}
bool cmp(int a, int b) { return dfn[a] < dfn[b]; }
int find(int u, int v) {
for(int i = lim; i >= 0; i--)
if(dep[f[v][i]] > dep[u])
v = f[v][i];
return v;
}
void dfs_son_to_u(int u, int fa) {
if(isgj[u]) dist[u] = mkp(u, 0);
else dist[u] = mkp(0, inf);
for(int i = 0; i < (int)G[u].size(); i++) {
int v = G[u][i];
dfs_son_to_u(v, u);
if(dist[u].se > dist[v].se + dep[v] - dep[u] || dist[u].se == dist[v].se + dep[v] - dep[u] && dist[v].fi < dist[u].fi)
dist[u] = mkp(dist[v].fi, dist[v].se + dep[v] - dep[u]);
}
}
void dfs_fa_to_u(int u, int fa) {
int col = dist[u].fi;
int tmp = sz[u];
for(int i = 0; i < (int)G[u].size(); i++) {
int v = G[u][i]; tmp -= sz[find(u, v)];
if(dist[u].se + dep[v] - dep[u] < dist[v].se || dist[u].se + dep[v] - dep[u] == dist[v].se && dist[u].fi < dist[v].fi)
dist[v] = mkp(dist[u].fi, dist[u].se + dep[v] - dep[u]);
dfs_fa_to_u(v, u);
}
// cout<<u<<" "<<col<<"+="<<tmp<<endl;
res[isgj[col]] += tmp;
}
void dfs2(int u, int fa) {
//cout<<"dfs2: "<<u<<" "<<fa<<endl;
//怎么算虚树上某条边上挂出去的子树,,什么破防瞬间,,
if(fa != 0) {
if(dist[u].fi == dist[fa].fi) {
int t = find(fa, u);
if(dep[t] <= dep[u]) res[isgj[dist[u].fi]] += sz[t] - sz[u];//, cout<<sz[t] - sz[u]<<endl;
// cout<<t<<" "<<u<<endl;
} else {
int colu = dist[u].fi, colfa = dist[fa].fi;
// cout<<colu<<"&"<<colfa<<endl;
int t = u;
if(dist[u].fi < dist[fa].fi) {
for(int i = lim; i >= 0; i--)
if(f[t][i] && dep[f[t][i]] >= dep[fa] && getdis(colu, f[t][i]) <= getdis(colfa, f[t][i]))
t = f[t][i];
} else {
for(int i = lim; i >= 0; i--)
if(f[t][i] && dep[f[t][i]] >= dep[fa] && getdis(colu, f[t][i]) < getdis(colfa, f[t][i]))
t = f[t][i];
}
if(dep[t] <= dep[u]) res[isgj[colu]] += sz[t] - sz[u];//, cout<<t<<" "<<u<<endl, cout<<sz[t] - sz[u]<<endl;
int ze = find(fa, u);
if(dep[ze] <= dep[f[t][0]]) res[isgj[colfa]] += sz[ze] - sz[t];//, cout<<ze<<" "<<t<<endl, cout<<sz[ze] - sz[t]<<endl;
}
}
for(int i = 0; i < (int)G[u].size(); i++) {
int v = G[u][i];
dfs2(v, u);
}
}
void doit() {
//cout<<"<============ doit ============>"<<endl;
scanf("%d", &k);
for(int i = 1; i <= k; i++)
scanf("%d", &gj[i]), isgj[gj[i]] = i;
sort(gj + 1, gj + k + 1, cmp);
build();
dfs_son_to_u(1, 0); dfs_fa_to_u(1, 0);
dfs2(1, 0);
for(int i = 1; i <= k; i++)
printf("%d ", res[i]);
puts("");
for(int i = 1; i <= k; i++)
isgj[gj[i]] = 0, res[i] = 0;
}
int main() {
//freopen("ex.in", "r", stdin);
//freopen("ex.out", "w", stdout);
scanf("%d", &n);
for(int i = 1, u, v; i < n; i++) {
scanf("%d%d", &u, &v);
add(u, v), add(v, u);
}
dfs1(1, 0);
int q; scanf("%d", &q);
while(q--) doit();
return 0;
}
摆了,我觉得够了,不想写了。
QwQwQ