传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3572
思路:注意到m[1]+m[2]+…+m[q]<=300000
上虚树Dp。
先DP出虚树上每个点被哪个点管辖,记为bel[i]。
这个从上到下更新一次答案,在从下到上更新一次答案即可。
对于最终答案,我们遍历一遍虚树,把虚树每条边对应的点划分好即可
然后我们考虑虚树的一条边(a,b)
记x为既是a的儿子又是b的祖先的点,siz[a]表示a点的子树大小
1.如果bel[a]==bel[b]那么这条边所对应的一堆实际的点也应该归bel[a]管辖
那么f[bel[a]]+=siz[x]-siz[b]
2.如果bel[a]!=bel[b]那么这条边(在原树上是一条路径)中一定有一个分界点mid
使得mid及以下的点归bel[b]管辖,mid以上的点归bel[a]管辖
倍增找出mid即可
那么f[bel[a]]+=siz[x]-siz[mid],f[bel[b]]+=siz[mid]-siz[b]
最后要处理的是完全没有在虚树上出现的子树,这些子树肯定被控制它们的根的点所控制
我们记录一个rem[i]表示i的子树现在还有多少个点没被统计答案,初值为siz[i]
处理了一条边(a,b)后,那么x的子树就在处理虚树时已经被我们处理完了,记得减去,rem[a]-=siz[x]
最后把这些多余的点加回去即可,f[bel[a]]+=rem[a]
然后有一个细节,记得把虚树的根的rem设为整个树的大小,而不是它的siz,因为那样会使我们漏掉原来的根的其他子树
实现的时候注意细节(各种乱七八糟的问题)....
不知道为什么这么慢....(看来是写丑了)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=300010,maxm=600010,maxk=22;
using namespace std;
int n,m,fa[maxn][maxk],dep[maxn],siz[maxn],dfn[maxn],tim,f[maxn],cnt,poi[maxn],bel[maxn],stk[maxn],top,ordc,ord[maxn],seq[maxn],rem[maxn];
bool bo[maxn];char ch;
bool cmp(int a,int b){return dfn[a]<dfn[b];}
void read(int &x){
for (ch=getchar();!isdigit(ch);ch=getchar());
for (x=0;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
}
int lca(int a,int b){
if (dep[a]<dep[b]) swap(a,b);
for (int h=dep[a]-dep[b],i=19;i>=0;i--) if (h>=(1<<i)) h-=(1<<i),a=fa[a][i];
if (a==b) return a;
for (int i=19;i>=0;i--) if (fa[a][i]!=fa[b][i]) a=fa[a][i],b=fa[b][i];
return fa[a][0];
}
int getd(int a,int b){return dep[a]+dep[b]-(dep[lca(a,b)]<<1);}
struct Tgraph{
int pre[maxm],now[maxn],son[maxm],tot;
void add(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b;}
void dfs1(int x){
siz[x]=1,dfn[x]=++tim;
for (int i=1;i<=19;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
for (int y=now[x];y;y=pre[y]) if (son[y]!=fa[x][0])
dep[son[y]]=dep[x]+1,fa[son[y]][0]=x,dfs1(son[y]),siz[x]+=siz[son[y]];
}
void dfs2(int x){//找子树中最近的关键点
ord[++ordc]=x,rem[x]=siz[x];
for (int y=now[x];y;y=pre[y]){
int v=son[y];dfs2(v);
if (!bel[v]) continue;
int t1=getd(x,bel[v]),t2=getd(x,bel[x]);
if (!bel[x]||t1<t2||(t1==t2&&bel[v]<bel[x])) bel[x]=bel[v];
}
}
void dfs3(int x){//找上面最近的关键点
for (int y=now[x];y;y=pre[y]){
int v=son[y],t1=getd(v,bel[x]),t2=getd(bel[v],v);
if (!bel[v]||t1<t2||(t1==t2&&bel[x]<bel[v])) bel[v]=bel[x];
dfs3(v);
}
}
void solve(int a,int b){//处理虚边a->b
int x=b,mid=b;
for (int i=19;i>=0;i--) if (dep[fa[x][i]]>dep[a]) x=fa[x][i];
rem[a]-=siz[x];
if (bel[a]==bel[b]){f[bel[a]]+=siz[x]-siz[b];return;}
for (int i=19;i>=0;i--){
int nxt=fa[mid][i];
if (dep[nxt]<=dep[a]) continue;
int t1=getd(bel[a],nxt),t2=getd(bel[b],nxt);
if (t2<t1||(t1==t2&&bel[b]<bel[a])) mid=nxt;
}
f[bel[a]]+=siz[x]-siz[mid],f[bel[b]]+=siz[mid]-siz[b];
}
void getans(){
for (int i=1;i<=ordc;i++) for (int y=now[ord[i]];y;y=pre[y]) solve(ord[i],son[y]);
for (int i=1;i<=ordc;i++) f[bel[ord[i]]]+=rem[ord[i]];
for (int i=1;i<=cnt;i++) printf("%d ",f[seq[i]]);puts("");
}
}g1,g2;
void work(){
stk[top=1]=poi[1];
for (int i=2;i<=cnt;i++){
int u=lca(poi[i],stk[top]);
while (dfn[stk[top]]>dfn[u]){
if (dfn[stk[top-1]]<=dfn[u]){
g2.add(u,stk[top]);
if (u!=stk[--top]) stk[++top]=u;
break;
}
g2.add(stk[top-1],stk[top]),top--;
}
stk[++top]=poi[i];
}
while (top>1) g2.add(stk[top-1],stk[top]),top--;
g2.dfs2(stk[1]),g2.dfs3(stk[1]),rem[stk[1]]=siz[1],g2.getans();
for (int i=1;i<=ordc;i++){int p=ord[i];f[p]=g2.now[p]=rem[p]=bel[p]=0;}
g2.tot=ordc=0;
}
int main(){
read(n);
for (int i=1,a,b;i<n;i++) read(a),read(b),g1.add(a,b),g1.add(b,a);
g1.dfs1(1),read(m);
while (m--){
read(cnt);
for (int i=1;i<=cnt;i++) read(poi[i]),seq[i]=poi[i],bo[poi[i]]=1,bel[poi[i]]=poi[i];
sort(poi+1,poi+1+cnt,cmp),work();
}
return 0;
}
/*
10
2 1
3 2
4 3
5 4
6 1
7 3
8 3
9 4
10 1
5
2
6 1
5
2 7 3 6 9
1
8
4
8 7 10 3
5
2 9 3 5 8
*/