bzoj3572: [Hnoi2014]世界树

题目链接

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; 
} 
 

猜你喜欢

转载自www.cnblogs.com/sssy/p/9235747.html
今日推荐