ZJOI2012 灾难

题链

我们考虑拓扑排序,那么我们可以构造一个树结构,使其满足:

当一个点灭亡,其子树也灭亡。

我们考虑对一个点的所有边求lca,把这个点加到lca的子树里就好了。

答案就是子树的大小-1(减掉自己)

#include<bits/stdc++.h>
#define eho(X,x) for(int i=X.head[x];i;i=X.net[i])
#define sight(c) ('0'<=c&&c<='9')
#define pc printf
using namespace std;
#define N 100007
inline void read(int &x){
    static char c;
    for (c=getchar();!sight(c);c=getchar());
    for (x=0;sight(c);c=getchar()) x=x*10+c-48;
}
int v,n,t,dep[N],to[N],q[N],be,tog,f[N][21],LA,siz[N];
struct G{
    int fall[N<<1],head[N],net[N],tot;
    inline void add(int x,int y){
        fall[++tot]=y; net[tot]=head[x]; head[x]=tot;
    }
}G1,G2,G3;
int lca(int x,int y){
    if (dep[x]<dep[y]) swap(x,y);
    for (int i=18;~i;i--) if (dep[f[x][i]]>=dep[y]) x=f[x][i];
    if (x==y) return x;
    for (int i=18;~i;i--) if (f[x][i]^f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}
void dfs(int x){
    siz[x]=1;
    eho(G2,x) dfs(G2.fall[i]),siz[x]+=siz[G2.fall[i]];
}
void write(int x){if (x<10){putchar(48+x);return;}write(x/10);putchar(48+x%10);}
inline void writeln(int x){if (x<0) putchar('-'),x=-x; write(x); putchar('\n');}
int main () {
//  freopen("a.in","r",stdin);
    read(n);
    for (int i=1;i<=n;i++) 
        while (read(t),t) 
          G1.add(t,i),G3.add(i,t),to[i]++; 
    for (int i=1;i<=n;i++)
      if (!to[i]) q[++tog]=i;
//  cerr<<"rrsb"<<endl;
    while (tog<n) {
        ++be;
        eho(G1,q[be])  { 
        to[G1.fall[i]]--;
        if (!to[G1.fall[i]]) q[++tog]=G1.fall[i];}
    } 
//  cerr<<"rrsb"<<endl;
    for (int t=1;t<=n;t++) {
        v=q[t]; 
        if (!G3.head[v]) {G2.add(0,v); dep[v]=1;continue;}
        else {LA=G3.fall[G3.head[v]];}
        eho(G3,v) LA=lca(LA,G3.fall[i]);
        G2.add(LA,v);  f[v][0]=LA; dep[v]=dep[LA]+1;
        for(int i=1;i<=18;i++) f[v][i]=f[f[v][i-1]][i-1];
    }
    dfs(0);
    for (int i=1;i<=n;i++) writeln(siz[i]-1);
    return 0;
}

 

转载于:https://www.cnblogs.com/rrsb/p/8367226.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值