BZOJ2815: [ZJOI2012]灾难(支配树)

传送门

题解:
支配树裸题了。。

写这篇博客只是为了提供一份支配树的代码。。

#include<bits/stdc++.h>
using namespace std;
inline int rd(){
    char ch=getchar();int i=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
    return i*f;
}
inline void W(int x){
    static int buf[50];
    if(!x){putchar('0');return;}
    if(x<0){putchar('-');x=-x;}
    while(x){buf[++buf[0]]=x%10;x/=10;}
    while(buf[0])putchar(buf[buf[0]--]+'0');
}
inline void gmin(int &x,int y){(x>y)?(x=y):0;}
const int N=1e5+50;
int rt,n,sdom[N],idom[N],id[N],dfn[N],mnsd[N],tot,anc[N],fa[N];
vector<int>pre[N],buc[N],edge[N];
struct DominatorTree{
    int sze[N],v[N],nt[N],g[N],ec;
    inline void add(int x,int y){nt[++ec]=g[x];g[x]=ec;v[ec]=y;}
    inline void dfs(int x){
        sze[x]=1;
        for(int j=g[x];j;j=nt[j])dfs(v[j]),sze[x]+=sze[v[j]];
    }
}g;
inline void dfs(int x,int f=0){
    dfn[x]=++tot;id[tot]=x;fa[x]=f;
    sdom[x]=mnsd[x]=anc[x]=x;
    for(int e=edge[x].size()-1;e>=0;e--){
        int v=edge[x][e];
        if(dfn[v])continue;
        dfs(v,x);
    }
}
inline void getanc(int p){
    if(anc[p]!=p)getanc(anc[p]);
    if(dfn[sdom[mnsd[anc[p]]]]<dfn[sdom[mnsd[p]]])mnsd[p]=mnsd[anc[p]];
    anc[p]=anc[anc[p]];
}
inline int eval(int p){  //获取到根节点路径上sdom最小的点
    getanc(p);
    return mnsd[p];
}
int main(){
    n=rd();rt=n+1;
    for(int i=1;i<=n;i++){
        int cnt=0,x=rd();
        while(x){
            pre[i].push_back(x);
            edge[x].push_back(i);
            ++cnt;x=rd();
        }
        if(!cnt){
            pre[i].push_back(rt);
            edge[rt].push_back(i);
        }
    }
    dfs(rt);fa[rt]=rt;
    for(int i=tot;i>=1;i--){
        int u=id[i];
        for(int j=pre[u].size()-1;j>=0;j--){
            int t=eval(pre[u][j]);
            if(dfn[sdom[t]]<dfn[sdom[u]])sdom[u]=sdom[t];
        }
        anc[u]=fa[u];int f=fa[u];
        buc[sdom[u]].push_back(u);
        for(int j=buc[f].size()-1;j>=0;j--){ //此时该节点所有子树以父节点为半支配点的点路径全部已经处理出
            int v=buc[f][j];int t=eval(v);
            if(dfn[sdom[t]]<dfn[sdom[v]])idom[v]=t;
            else idom[v]=sdom[v];
        }
        buc[f].clear();
    }
    for(int i=2;i<=tot;i++){
        int u=id[i];
        if(idom[u]!=sdom[u])idom[u]=idom[idom[u]];
        g.add(idom[u],u);
    }
    g.dfs(id[1]);
    for(int i=1;i<=n;i++){
        W(g.sze[i]-1);putchar('\n');
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值