题目大意
给你一棵形态确定的
N
个点的有根树,1号节点为根,有
N,Q≤3×105,∑k≤3×105
Solution
果然是好久没打题了,看了题目又不会做。
观察题目可以发现,由于
∑k≤3×105
,所以应该把关注的重点放在每次染色的
k
个点上。注意到从下到上,每次子树中染色数发生变化时,都是当前点位于某些被染色节点的LCA处。所以我们只要把所有节点的LCA取出重新建树即可计算答案。
这样的节点有多少个?
考虑排序后被染色的点
ai
,
aj
(
i≠j
)
1.
|i−j|=1
时,已经被计算过,加入了答案;
2.
|i−j|>1
时,不妨设
i<j
,那么根据定义只会出现如下情况:
a)
ai
是LCA,此时所有节点
ak(i<k<j)
均在
ai
某个和
aj
相同的子树中,那么
ai
和
ai+1
的LCA就是
ai
;
b)
ai
和
aj
在它们的LCA
aLCA
的两个不同子树中,那么当
i≤k<j
时,必然存在某个
k
使得
因此我们最多有
2k
个节点,通过它们按照原树的父子关系建立新树即可,点权为子树中被染色节点数,边权为两点原树间距离。
这样我们就可以在
O((n+∑k)logn)
内完成。
#include<set>
#include<map>
#include<queue>
#include<cmath>
#include<string>
#include<cstdio>
#include<vector>
#include<cassert>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rep(i,a,b) for (int i=a; i<=b; i++)
#define per(i,a,b) for (int i=a; i>=b; i--)
#define debug(x) {cout<<(#x)<<" "<<x<<endl;}
using namespace std;
typedef long long LL;
inline int read() {
int x=0,f=1; char ch=getchar();
while (!(ch>='0'&&ch<='9')) {if (ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9') {x=x*10+(ch-'0'); ch=getchar();}
return x*f;
}
const int N = 300005;
int n,q,k,ind=0;
vector<int> e[N],e2[N];
int dep[N],f[N][21];
int inp[N],oup[N];
inline void dfs(int x,int fa) {
inp[x]=++ind; dep[x]=dep[fa]+1; f[x][0]=fa;
rep(i,1,20) f[x][i]=f[f[x][i-1]][i-1];
for (int i=0;i<(int)e[x].size();i++) if (e[x][i]!=fa) dfs(e[x][i],x);
oup[x]=++ind;
}
inline int LCA(int u,int v) {
if (dep[u]<dep[v]) swap(u,v);
int d=dep[u]-dep[v];
per(i,20,0) if (d&(1<<i)) u=f[u][i];
per(i,20,0) if (f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
if (u==v) return u;
return f[u][0];
}
int cnt[N],num[N];
inline void dfs2(int x,int fa) {
for (int i=0;i<(int)e2[x].size();i++) {
dfs2(e2[x][i],x); num[x]+=num[e2[x][i]];
}
cnt[num[x]]+=dep[x]-dep[fa];
}
int ver[N<<1];
bool cmp(int a,int b) {
return inp[a]<inp[b];
}
int stk[N];
int main() {
#ifndef ONLINE_JUDGE
// freopen("input43.txt","r",stdin);
// freopen("data.out","w",stdout);
#endif
n=read(); rep(i,1,n-1) {int u=read(),v=read(); e[u].push_back(v); e[v].push_back(u);}
dep[0]=0; dfs(1,0);
q=read();
while (q--) {
int k=read();
int vsize=0;
rep(i,1,k) {
int x=read(); ver[++vsize]=x;
num[x]=1;
}
sort(ver+1,ver+vsize+1,cmp);
per(i,vsize,2) ver[++vsize]=LCA(ver[i],ver[i-1]);
ver[++vsize]=1; sort(ver+1,ver+vsize+1,cmp);
int nsize=1; rep(i,2,vsize) if (ver[i]!=ver[i-1]) ver[++nsize]=ver[i];
vsize=nsize;
int top=0; stk[++top]=ver[1];
rep(i,2,vsize) {
while (top>0&&oup[stk[top]]<=inp[ver[i]]) top--;
stk[++top]=ver[i];
e2[stk[top-1]].push_back(stk[top]);
}
rep(i,0,k) cnt[i]=0;
dfs2(1,0); cnt[0]=n; rep(i,1,k) cnt[0]-=cnt[i];
rep(i,0,k) printf("%d ",cnt[i]); puts("");
rep(i,1,vsize) {e2[ver[i]].clear(); num[ver[i]]=0;}
}
return 0;
}