bzoj3572: [Hnoi2014]世界树
题目链接
题解
构建虚树
对于虚树dp
我们首先对于虚中的点,处理处每个点属于哪个点管辖,这个两边dp就好了,一次用儿子更新父亲,一次父亲更新儿子
然后用虚树中的点扩展到整棵树,考虑
若叙述中相邻两点归属于同一个点管辖,那么他们中间的点也肯定都归该点管辖
若不同,在这两点间的链上一定存在一个点将两部分分开,我们倍增将这个点求出
我是不会告诉你们这样写add(stack[top - 1],stack[top--]);会GG的,QAQ
要add(stack[top - 1],stack[top]);top--;好坑啊,调了一天
辣鸡虚树,毁我青春Qwq
/
#include<cstdio>
#include<cstring>
#include<algorithm>
inline int read() {
int x = 0,f = 1;
char c = getchar();
while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar(); }
while(c <= '9' && c >= '0')x = x * 10 + c - '0',c = getchar();
return x * f;
}
const int maxn = 300007;
int n;
struct node {
int v,next;
} edge[maxn << 1],e[maxn << 1];
int head[maxn],num,h[maxn],num1,deep[maxn];
inline void add(int u,int v) { if(u == v) return;
e[++ num1].v = v;e[num1].next = h[u];h[u] = num1;
}
inline void add_edge(int u,int v ) {
edge[++ num].v = v;edge[num].next = head[u];head[u] = num;
}
int dfn[maxn],cnt = 0,dad[maxn][20],size[maxn];
void dfs(int x,int fa) {
size[x] = 1;
dfn[x] = ++ cnt; deep[x] = deep[fa] + 1; dad[x][0] = fa;
for(int i = 0;dad[x][i];++ i)
dad[x][i + 1] = dad[dad[x][i]][i];
for(int i = head[x];i;i = edge[i].next ) {
int v = edge[i].v;
if(v == fa) continue;
dfs(v,x);
size[x] += size[v];
}
}
int lca(int x,int y) {
if(deep[x] > deep[y]) std::swap(x,y);
for(int i = 18;i >= 0;-- i)if(deep[x] <= deep[dad[y][i]]) y = dad[y][i];
if(x == y) return x;
for(int i = 18;i >= 0;-- i) if(dad[x][i] != dad[y][i])x = dad[x][i],y = dad[y][i];
return x == y ? x : dad[x][0];
}
inline bool cmp(int x,int y) {return dfn[x] < dfn[y];}
int c[maxn],rem[maxn],Dfn,bel[maxn];
int dis(int x,int y) {
return deep[x] + deep[y] - 2 * deep[lca(x,y)];
}
void get_fri(int x) {
c[++ Dfn] = x;rem[x] = size[x];
for(int i = h[x];i;i = e[i].next ) {
int v = e[i].v;
get_fri(v);
if(!bel[v]) continue;
int t1 = dis(bel[v],x),t2 = dis(bel[x],x);
if((t1 == t2 && bel[v] < bel[x]) || t1 < t2 || !bel[x]) bel[x] = bel[v];
}
}
void get_sec(int x) {
for(int i = h[x];i;i = e[i].next) {
int v = e[i].v;
int t1 = dis(bel[x],v),t2 = dis(bel[v],v);
if((t1 == t2 && bel[v] > bel[x]) || t1 < t2 || !bel[v])bel[v] = bel[x];
get_sec(v);
}
}
int dp[maxn];
void divide(int a,int b) {
int x = b,mid = b;
for(int i = 18;i >= 0;-- i)
if(deep[dad[x][i]] > deep[a]) x = dad[x][i];
rem[a] -= size[x];
if(bel[a] == bel[b]) {
dp[bel[a]] += size[x] - size[b]; return;
}
for(int i = 18;i >= 0;-- i) {
if(deep[dad[mid][i]] <= deep[a]) continue;
int t1 = dis(bel[a],dad[mid][i]),t2 = dis(bel[b],dad[mid][i]);
if(t1 > t2 || (t1 == t2 && bel[b] < bel[a]))mid = dad[mid][i];
}
dp[bel[a]] += size[x] - size[mid];
dp[bel[b]] += size[mid] - size[b];
}
int htmp[maxn],stack[maxn],H[maxn];
void solve(int k = 0) {
int top;memset(h,0,sizeof h);
top = Dfn = 0;
k = read();
for(int i = 1;i <= k;++ i) htmp[i] = H[i] = read();
for(int i = 1;i <= k;++ i) bel[H[i]] = H[i] ;
std::sort(H + 1,H + k + 1,cmp);
stack[++ top] = 1;
for(int i = 1;i <= k;++ i) {
int x = H[i],f = lca(stack[top],x);
while("tle") {
if(deep[f] >= deep[stack[top - 1]]) {
add(f,stack[top --]);
if(stack[top] != f) stack[++ top] = f;
break;
} add(stack[top - 1],stack[top]);top --;
} if(stack[top] != x) stack[++ top] = x;
}
while(top - 1) add(stack[top - 1],stack[top]),top --;
get_fri(1);
get_sec(1);
for(int i = 1;i <= Dfn;++ i)
for(int j = h[c[i]];j;j = e[j].next) {
divide(c[i],e[j].v);
}
for(int i = 1;i <= Dfn;++ i) dp[bel[c[i]]] += rem[c[i]];
for(int i = 1;i <= k;++ i) printf("%d ",dp[htmp[i]]);puts("");
for(int i = 1;i <= Dfn;++ i) dp[c[i]] = bel[c[i]] = H[c[i]] = rem[c[i]] = 0;
num1 = 0;
}
int main() {
n = read();
for(int u,v,i = 1;i < n;++ i) {
u = read(),v = read();
add_edge(u,v);add_edge(v,u);
}
dfs(1,0);
int q = read() ;
for(;q --;)
solve();
return 0;
}