给你一棵形态确定的 N 个点的有根树,1号节点为根,有 Q 个询问,每次询问如果把给定的 k 个点染色,子树中被染色节点数为 0,1,2,...,k 的节点分别有多少。
N,Q≤3×105,∑k≤3×105
果然是好久没打题了,看了题目又不会做。
观察题目可以发现,由于 ∑k≤3×105 ,所以应该把关注的重点放在每次染色的 k 个点上。注意到从下到上,每次子树中染色数发生变化时,都是当前点位于某些被染色节点的LCA处。所以我们只要把所有节点的LCA取出重新建树即可计算答案。
这样的节点有多少个? k2 ?如果我们用dfs序表示出这颗树,我们可以发现当所有染色点按照dfs序的时间戳排序后,相邻两个的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 使得 ak 和 ak+1 在 aLCA 的两个不同子树中(否则它们均和 ai 在 aLCA 的相同子树中),那么这时 ak 和 ak+1 的LCA即为 aLCA .
因此我们最多有 2k 个节点,通过它们按照原树的父子关系建立新树即可,点权为子树中被染色节点数,边权为两点原树间距离。
这样我们就可以在 O((n+∑k)logn) 内完成。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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)<<" "<
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]int d=dep[u]-dep[v];
per(i,20,0) if (d&(1<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]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;
}