虚树 + DP
想了很久很久还调了半天
就算有倍增数组我也要树剖求lca
先建出虚树,然后虚树上的每个节点维护到自己最近的关键点的距离 \(dis[u]\) 和所属的关键点 \(be[u]\) 。
dp1(int u)
从底往上去更新, dp2(int u)
从上向下去更新。
计算答案时,我们发现若虚树上一条边 \((u,v)\)(即原图中的一条链 \((u,\cdots,v)\) )满足 \(be[u]\neq be[v]\) ,那么 \((u,\cdots,v)\) 中一定被分为了不相交的两部分,即
\(be[i]=be[u],i\in(u,\cdots,x)\\be[j]=be[v],j\in (y,\cdots,v)\\(u,\cdots,x)+(y,\cdots,v)=(u,\cdots,v)\)
为了快速获得分界点,我们在原树上倍增去找即可;记得排除同一棵子树的贡献,即在父节点时要减掉子树的 \(sz\) 。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define R register int
using namespace std;
namespace Luitaryi {
inline int g() { R x=0,f=1;
register char s; while(!isdigit(s=getchar())) f=s=='-'?-1:f;
do x=x*10+(s^48); while(isdigit(s=getchar())); return x*f;
} const int N=300010,B=18,Inf=0x3f3f3f3f;
int n,m,k;
int vr[N<<1],nxt[N<<1],fir[N],w[N<<1],cnt,num;
int pre[N],d[N],sz[N],son[N],dfn[N],top[N],fa[N][B+1],h[N],stk[N],siz[N],ans[N];
int dis[N],be[N],mem[N]; bool vis[N];
inline void add(int u,int v,int ww)
{vr[++cnt]=v,nxt[cnt]=fir[u],fir[u]=cnt,w[cnt]=ww;}
inline void dfs(int u) {
dfn[u]=++num,sz[u]=1;
for(R t=1;t<=B;++t)
fa[u][t]=fa[fa[u][t-1]][t-1];
for(R i=fir[u];i;i=nxt[i]) {
R v=vr[i];
if(dfn[v]) continue;
pre[v]=u,d[v]=d[u]+1;
fa[v][0]=u;
dfs(v);
sz[u]+=sz[v];
if(sz[son[u]]<sz[v]) son[u]=v;
}
}
inline void dfs2(int u,int tp) {
top[u]=tp;
if(son[u]) dfs2(son[u],tp);
for(R i=fir[u];i;i=nxt[i]) {
R v=vr[i];
if(!top[v]) dfs2(v,v);
}
}
inline bool cmp(const int& a,const int& b)
{return dfn[a]<dfn[b];}
inline int lca(int u,int v) {
while(top[u]!=top[v]) {
if(d[top[u]]<d[top[v]]) swap(u,v);
u=pre[top[u]];
} return d[u]<d[v]?u:v;
}
inline int dist(int u,int v) {return d[u]+d[v]-2*d[lca(u,v)];}
inline int jmp(int u,int dep) {
for(R t=B;~t;--t) if(d[fa[u][t]]>=dep) u=fa[u][t];
return u;
}
inline void dp1(int u) {
siz[u]=sz[u];
if(vis[u]) dis[u]=0,be[u]=u;
else dis[u]=Inf,be[u]=0;
for(R i=fir[u];i;i=nxt[i]) {
R v=vr[i];
dp1(v);
if(dis[u]>dis[v]+w[i]||(dis[u]==dis[v]+w[i]&&be[u]>be[v]))
dis[u]=dis[v]+w[i],be[u]=be[v];
}
}
inline void dp2(int u) {
for(R i=fir[u];i;i=nxt[i]) {
R v=vr[i];
if(dis[v]>dis[u]+w[i]||(dis[v]==dis[u]+w[i]&&be[u]<be[v]))
dis[v]=dis[u]+w[i],be[v]=be[u];
dp2(v);
if(be[u]==be[v]) siz[u]-=sz[v];
else {
R t=dis[u]+(d[v]-d[u])-dis[v];
R tmp=jmp(v,d[v]-(t-1)/2);
if(t&&t%2==0&&be[v]<be[u]) tmp=pre[tmp];
siz[v]+=sz[tmp]-sz[v];
siz[u]-=sz[tmp];
}
ans[be[v]]+=siz[v];
}
}
inline void main() {
n=g();
for(R i=1,u,v;i<n;++i)
u=g(),v=g(),add(u,v,0),add(v,u,0);
d[1]=1,dfs(1),dfs2(1,1);
memset(fir,0,sizeof fir);
m=g();
while(m--) {
k=g();
for(R i=1;i<=k;++i) vis[mem[i]=h[i]=g()]=true;
sort(h+1,h+k+1,cmp);
fir[1]=cnt=0; R top;
stk[top=1]=1;
for(R i=1+(h[1]==1),l;i<=k;++i) {
l=lca(h[i],stk[top]);
if(l!=stk[top]) {
while(dfn[l]<dfn[stk[top-1]])
add(stk[top-1],stk[top],dist(stk[top],stk[top-1])),--top;
if(dfn[l]>dfn[stk[top-1]])
fir[l]=0,
add(l,stk[top],dist(stk[top],l)),stk[top]=l;
else
add(l,stk[top],dist(stk[top],l)),--top;
}
fir[h[i]]=0,stk[++top]=h[i];
}
for(R i=1;i<top;++i)
add(stk[i],stk[i+1],dist(stk[i+1],stk[i]));
dp1(1),dp2(1); ans[be[1]]+=siz[1];
for(R i=1;i<=k;++i) printf("%d ",ans[mem[i]]),ans[mem[i]]=0; puts("");
for(R i=1;i<=k;++i) vis[h[i]]=0;
}
}
} signed main() {Luitaryi::main(); return 0;}
2020.01.17
原文:https://www.cnblogs.com/Jackpei/p/12207436.html