P3233 [HNOI2014]世界树
首先,这题必然要虚树。
然后我们考虑建虚树的时候,树上有两种点,原临时议事处的点和虚树新产生的点。
原本如果只有单次询问的话,我们是可以暴力处理出每一个点向上向下的最近点的,但是如果在虚树上我们是必然不能这样的。
我们考虑根据两个议事处的距离分配区域。但是我们考虑到这可能会出现第三方势力争夺。
哦,我们可以在虚树上先做我们这个暴力,这样的话每一个节点都是有颜色的了。然后又由于现在的虚树上相邻两点之间在原树上都是链了,如果有一条虚树边两端的颜色不一样,我们就考虑找到分界点的位置即可。可知此时就不会出现我们上面顾虑的第三方势力的问题。
然后我们考虑怎么算这个暴力,实际上这里应该是需要一个换根 \(\text{dp}\) 的。我们这里需要找到一个子树中除去一个子子树剩下部分距离该子树根的最近点。这个必然可以用 \(\text{cdq}\) 分治解决,但是神 \(\text{wuhao2005}\) 告诉傻逼的我可以直接维护前后缀。我是傻逼。
我又是傻逼了。我们直接维护最近和次近就行了。
然后可能就做完了?虚树维护的细节可能要注意。
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int n,m,q,a[N],b[N],cnt[N];
struct Edge{int nxt,to;}e[N<<1];int fir[N];
void add(int u,int v,int i){e[i]=(Edge){fir[u],v},fir[u]=i;}
struct Node{int fa[19],mp,dep,siz;}tr[N];
int st[N*2][20],lg[N*2],cnt_dfn=0;
void dfs(int u){
st[++cnt_dfn][0]=u,tr[u].mp=cnt_dfn;
tr[u].dep=tr[tr[u].fa[0]].dep+1,tr[u].siz=1;
for(int i=fir[u];i;i=e[i].nxt){
int v=e[i].to;if(v==tr[u].fa[0]) continue;
tr[v].fa[0]=u,dfs(v),tr[u].siz+=tr[v].siz,st[++cnt_dfn][0]=u;
}
}
bool cmp(int x,int y){return tr[x].mp<tr[y].mp;}
void init(){
lg[1]=0;for(int i=2;i<=cnt_dfn;++i) lg[i]=lg[i>>1]+1;
for(int j=1;j<20;++j){
for(int i=1;i+(1<<j)-1<=cnt_dfn;++i){
if(tr[st[i][j-1]].dep<tr[st[i+(1<<(j-1))][j-1]].dep)
st[i][j]=st[i][j-1];else st[i][j]=st[i+(1<<(j-1))][j-1];
}
}
for(int j=1;j<19;++j){
for(int i=1;i<=n;++i)
tr[i].fa[j]=tr[tr[i].fa[j-1]].fa[j-1];
}
}
int lca(int u,int v){
if(tr[u].mp>tr[v].mp) swap(u,v);
int tmp=lg[tr[v].mp-tr[u].mp+1];
if(tr[st[tr[u].mp][tmp]].dep<tr[st[tr[v].mp-(1<<tmp)+1][tmp]].dep)
return st[tr[u].mp][tmp];else return st[tr[v].mp-(1<<tmp)+1][tmp];
}
int dis(int u,int v){
return tr[u].dep+tr[v].dep-2*tr[lca(u,v)].dep;
}
struct Virtual_Tree{
struct Edge{int nxt,to;}e[N];int fir[N],e_size=0;
void add(int u,int v){e[++e_size]=(Edge){fir[u],v},fir[u]=e_size;}
int f[N];bool tag[N];
bool cmp(int u,int v,int w){
int disu=dis(u,w),disv=dis(v,w);
return disu==disv?u<v:disu<disv;
}
void dfs1(int u,int fa){
if(tag[u]) f[u]=u;else f[u]=-1;
for(int i=fir[u];i;i=e[i].nxt){
int v=e[i].to;if(v==fa) continue;
dfs1(v,u);if(f[u]==-1||cmp(f[v],f[u],u)) f[u]=f[v];
}
}
void dfs2(int u,int fa,int anc){
if(tag[u]) anc=u;
if(anc!=-1&&(f[u]==-1||cmp(anc,f[u],u))) f[u]=anc;
int tmp1=-1,tmp2=-1;
for(int i=fir[u];i;i=e[i].nxt){
int v=e[i].to;if(v==fa) continue;
if(tmp1==-1||cmp(f[v],tmp1,u)) tmp2=tmp1,tmp1=f[v];
else if(tmp2==-1||cmp(f[v],tmp2,u)) tmp2=f[v];
}
for(int i=fir[u];i;i=e[i].nxt){
int v=e[i].to;if(v==fa) continue;
if(tmp1==f[v]){
if(anc==-1||cmp(tmp2,anc,u)) dfs2(v,u,tmp2);
else dfs2(v,u,anc);
}
else{
if(anc==-1||cmp(tmp1,anc,u)) dfs2(v,u,tmp1);
else dfs2(v,u,anc);
}
}
}
void dfs(int u,int fa){
if(!fa) cnt[f[u]]+=tr[u].siz;
if(fa&&f[u]!=f[fa]){
int tmp=u;
for(int i=18;i>=0;--i){
if(cmp(f[u],f[fa],tr[tmp].fa[i]))
tmp=tr[tmp].fa[i];
}
cnt[f[fa]]-=tr[tmp].siz,cnt[f[u]]+=tr[tmp].siz;
}
for(int i=fir[u];i;i=e[i].nxt){
int v=e[i].to;if(v!=fa) dfs(v,u);
}
}
}t;
int s[N],top=0;
void build(int m,int a[]){
sort(a+1,a+1+m,cmp);
t.e_size=0,t.fir[1]=0,s[top=1]=1;
for(int i=1;i<=m;++i){
if(a[i]==1) continue;
int tmp=lca(a[i],s[top]);
if(tmp!=s[top]){
while(tr[tmp].mp<tr[s[top-1]].mp)
t.add(s[top-1],s[top]),top--;
if(tr[tmp].mp>tr[s[top-1]].mp)
t.fir[tmp]=0,t.add(tmp,s[top]),s[top]=tmp;
else t.add(tmp,s[top--]);
}
t.fir[a[i]]=0,s[++top]=a[i];
}
for(int i=1;i<top;++i) t.add(s[i],s[i+1]);
}
int main(){
// freopen("data.in","r",stdin);
// freopen("shit.out","w",stdout);
cin>>n;
for(int i=1;i<n;++i){
int u,v;scanf("%d%d",&u,&v);
add(u,v,i<<1),add(v,u,i<<1|1);
}
dfs(1),init();
cin>>q;
while(q--){
cin>>m;
for(int i=1;i<=m;++i) scanf("%d",&a[i]),b[i]=a[i];
for(int i=1;i<=m;++i) t.tag[b[i]]=true;
build(m,a),t.dfs1(1,0),t.dfs2(1,0,-1),t.dfs(1,0);
for(int i=1;i<=m;++i) printf("%d ",cnt[b[i]]);
printf("\n");
for(int i=1;i<=m;++i) t.tag[b[i]]=false,cnt[b[i]]=0;
}
}
/*
状态不佳,明天再写。
*/