所谓虚树,就是虚了的树
(逃)
前言
在有些时候,我们只关心树上的某些特殊点,问题中的整体规模较大,但是这些特殊点往往比较稀疏,这个时候就可以使用虚树,只保留树上的关键点,从而大大缩小问题规模
引例
洛谷P3233 世界树
给出一个树和m个询问,每次询问给出k个特殊点,树上的每个结点都受最近的特殊点管辖,求每个特殊点管辖的结点数目
数据范围:n≤3∗105,m≤3∗105,∑k≤3∗105n\leq3*10^5,m\leq3*10^5,\sum k \leq3*10^5n≤3∗105,m≤3∗105,∑k≤3∗105
解析
首先考虑只有一次询问的时候如何做
只有我想到bfs了吗…
可以换根dp很简单的解决
但是本题每次dp一遍是nm的,无法接受
考虑使用虚树
首先,我们要找到所有的关键点
除了特殊点之外,我们还要选出一些非特殊点以保持树的形态不变
经过观察和题解,发现两两求出lca作为关键点即可
但是两两求是k2k^2k2的,无法接受
这里只有我想到根号分治了吗
引理:若x、y、z是dfs序单调递增的三个点,那么lca(x,z)lca(x,z)lca(x,z)必定是lca(x,y)lca(x,y)lca(x,y)和lca(y,z)lca(y,z)lca(y,z)两者之一
证明…自己画画图吧
所以我们把关键点按dfs序排一下序,然后相邻求lca即可
求出关键点,我们还要在它们之间连边
这可以在确定关键点的时候顺便求出来
具体的实现,需要利用一个栈来进行
栈里存储的是一条当前的极右链
一本通的实现比较恶心,学习了洛谷上第一篇题解的写法,相对比较简洁
zhan[top=1]=v[1];
for(int i=2;i<=k;i++){int x=v[i],lca=Lca(x,zhan[top]);while(top>1&&dep[lca]<=dep[zhan[top-1]]){addline(zhan[top-1],zhan[top]);top--;}if(zhan[top]!=lca){addline(lca,zhan[top]);zhan[top]=lca;}zhan[++top]=x;
}
while(top>1) addline(zhan[top-1],zhan[top]),top--;
int rt=zhan[1];
然后就是dp部分
不在虚树上的点可以比较容易的确定归属
关键是在链两端所属不同时的链上的点
可以找到链上管辖示例的分界点
然后用子树大小减一减就出来了
还是不难理解的,具体看代码吧
写的有点恶心,但跑的还是挺快的
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf (n+1)
//#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=3e5+100;
const int M=2e5+10500;
const double eps=1e-5;
inline ll read(){ll x(0),f(1);char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f;
}int n,m;
int tim;
struct node{int to,nxt,w;
}p[N<<1];
int fi[N],cnt;
int jd[N];
inline void addline(int x,int y,int w){if(jd[x]!=tim){jd[x]=tim;fi[x]=-1;}if(jd[y]!=tim){jd[y]=tim;fi[y]=-1;}//printf("x=%d y=%d\n",x,y);p[++cnt]=(node){y,fi[x],w};fi[x]=cnt;return;
}int pos[N];
int sz[N],pl[N][20],dep[N];
void init(int x,int f){pl[x][0]=f;for(int k=1;pl[x][k-1];k++) pl[x][k]=pl[pl[x][k-1]][k-1];pos[x]=++tim;sz[x]=1;dep[x]=dep[f]+1;for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(to==f) continue;init(to,x);sz[x]+=sz[to];}return;
}
inline int Lca(int x,int y){if(pos[x]<=pos[y]&&pos[y]<=pos[x]+sz[x]-1) return x;for(int k=19;k>=0;k--){int o=pl[x][k];if(!o||(pos[o]<=pos[y]&&pos[y]<=pos[o]+sz[o]-1)) continue;x=o;}return pl[x][0];
}
int bac[N],tag[N],v[N],ori[N];
int fa[N],zhan[N],top;
int dis[N],id[N];
bool cmp(int x,int y){return pos[x]<pos[y];
}
void dfs1(int x){if(tag[x]==tim){dis[x]=0;id[x]=x;}else dis[x]=1e9;for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;dfs1(to);if(dis[x]>dis[to]+p[i].w||(dis[x]==dis[to]+p[i].w&&id[x]>id[to])){dis[x]=dis[to]+p[i].w;id[x]=id[to];}}
}
void dfs2(int x,int f,int fv){if(f){if(dis[x]>dis[f]+fv||(dis[x]==dis[f]+fv&&id[x]>id[f])){dis[x]=dis[f]+fv;id[x]=id[f];}}for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;dfs2(to,x,p[i].w);}//printf("x=%d dis=%d id=%d\n",x,dis[x],id[x]);return;
}
inline int jump(int x,int w){for(int k=19;k>=0;k--){if(w<(1<<k)) continue;w-=(1<<k);x=pl[x][k];}return x;
}
void calc(int x){bac[id[x]]++;int lft=sz[x]-1;for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to,son=jump(to,p[i].w-1);lft-=sz[son];//printf(" x=%d to=%d -=%d+%d\n",x,to,siz[to],p[i].w-1);if(id[x]==id[to]) bac[id[x]]+=sz[son]-sz[to];else{int d=p[i].w+dis[to]-dis[x],add=(d-1+(id[x]<id[to]))/2;int pl=jump(to,p[i].w-1-add);bac[id[x]]+=sz[son]-sz[pl];bac[id[to]]+=sz[pl]-sz[to];// printf(" x=%d to=%d add=%d x+=%d to+=%d\n",x,to,add,sz[son]-sz[pl],sz[pl]-sz[to]);}calc(to);}//printf("x=%d leftsiz=%d\n",x,lft);bac[id[x]]+=lft;
}
int main(){
#ifndef ONLINE_JUDGE//freopen("a.in","r",stdin);//freopen("a.out","w",stdout);
#endifmemset(fi,-1,sizeof(fi));cnt=-1;n=read();for(int i=1;i<n;i++){int x=read(),y=read();addline(x,y,1);addline(y,x,1);}init(1,0);m=read();for(tim=1;tim<=m;tim++){//printf("\ntim=%d\n",tim);int k=read();for(int i=1;i<=k;i++){v[i]=ori[i]=read();bac[v[i]]=0;tag[v[i]]=tim;fa[v[i]]=0;}sort(v+1,v+1+k,cmp);cnt=-1;zhan[top=1]=v[1];for(int i=2;i<=k;i++){int x=v[i],lca=Lca(x,zhan[top]);while(top>1&&dep[lca]<=dep[zhan[top-1]]){addline(zhan[top-1],zhan[top],dep[zhan[top]]-dep[zhan[top-1]]);top--;}if(zhan[top]!=lca){addline(lca,zhan[top],dep[zhan[top]]-dep[lca]);zhan[top]=lca;}zhan[++top]=x;}while(top>1) addline(zhan[top-1],zhan[top],dep[zhan[top]]-dep[zhan[top-1]]),top--;//printf("OK\n");int rt(zhan[1]);dfs1(rt);dfs2(rt,0,0);bac[id[rt]]+=n-sz[rt];calc(rt);for(int i=1;i<=k;i++) printf("%d ",bac[ori[i]]);putchar('\n');}return 0;
}
/*
10
2 1
3 2
4 3
5 4
6 1
7 3
8 3
9 4
10 1
1
5
2 9 3 5 8
*/