P2597 [ZJOI2012]灾难

P2597 [ZJOI2012]灾难

题目描述

见链接


Solution

建立支配树即可


Addition

步骤:

  1. 反向建边
  2. 拓扑排序
  3. 按反拓扑序依次加入支配树节点

加入节点时, 取其所有原图父亲的 L C A LCA LCA作新树的父亲


Attention

两个字符 1 , 0 1,0 1,0调了 6 − h o u r s 6-hours 6hours !!!
要时刻注意循环边界 !!!


Code

#include<bits/stdc++.h>
#define reg register

const int maxn = 80000;

int N, cnt, num0, Lim;
int B[maxn], In[maxn];
int F[maxn][18];
int dep[maxn];
int head[maxn];
int size[maxn];

struct Edge{ int nxt, to; } edge[maxn<<1];

void Add(int from, int to){
        edge[++ num0] = (Edge){ head[from], to };
        head[from] = num0;
}

int LCA(int a, int b){
        if(dep[a] < dep[b]) std::swap(a, b);
        for(reg int i = 17; i >= 0; i --)
                if(dep[F[a][i]] >= dep[b]) a = F[a][i];
        if(a == b) return a;
        for(reg int i = 17; i >= 0; i --)
                if(F[a][i] != F[b][i]) a = F[a][i], b = F[b][i];
        return F[a][0];
}

void DFS(int k, int fa){
        size[k] = 1;
        for(reg int i = head[k]; i; i = edge[i].nxt){
                if(i <= Lim) continue ;
                int to = edge[i].to;
                if(to == fa) continue ;
                DFS(to, k);
                size[k] += size[to];
        }
}

int main(){
        scanf("%d", &N);
        for(reg int i = 1; i <= N; i ++){
                int x;
                scanf("%d", &x);
                while(x) Add(i, x), In[x] ++, scanf("%d", &x);
        }
        Lim = num0;
        std::queue <int> Q;
        for(reg int i = 1; i <= N; i ++) if(!In[i]) Q.push(i);
        while(!Q.empty()){
                int ft = Q.front(); Q.pop();
                B[++ cnt] = ft;
                for(reg int i = head[ft]; i; i = edge[i].nxt){
                        int to = edge[i].to;
                        if(!(-- In[to])) Q.push(to); 
                }
        }
        dep[N+1] = 1;
        for(reg int i = 0; i <= 17; i ++) F[N+1][i] = N+1;
        for(reg int i = cnt; i >= 1; i --){
                int s = N+1, x = B[i];
                if(head[x]){
                        s = edge[head[x]].to;
                        for(reg int j = head[x]; j; j = edge[j].nxt) 
                                s = LCA(s, edge[j].to);
                }
                dep[x] = dep[s] + 1, F[x][0] = s;
                Add(s, x);
                for(reg int j = 1; j <= 17; j ++) F[x][j] = F[F[x][j-1]][j-1];
        }
        DFS(N+1, 0);
        for(reg int i = 1; i <= N; i ++) printf("%d\n", size[i] - 1);
        return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值