hdu-4694 支配树模板

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=

 

题意:

       给你n个女生的联系情况,第n个人是大姐大(即题目中的根),从它开始会向其他人发送消息,如果第y个人失联了那么第x个人就无法收到消息,就称y是x的一个重要的姐妹,现在问你每个人的重要的姐妹的编号总和,如果不连通那么就是0。

做法:

       看了将近三个小时的支配树,总算是明白了一点点了。

       https://blog.csdn.net/litble/article/details/83019578   感觉别人讲的会比较透一点 感性理解叭  自己也加了点备注。

 


#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(int)a;i<=(int)b;i++)
using namespace std;
typedef long long ll;
const int maxn=50005;
const int maxm=100005;
int to[maxm],nex[maxm],head[maxn],cnt;
int n,m,dfn[maxn],fa[maxn],tot,Sfa[maxn];
int idom[maxn],semi[maxn],id[maxn],val[maxn];
vector<int> pre[maxn],dom[maxn];
void add(int u,int v){
    to[cnt]=v,nex[cnt]=head[u];
    head[u]=cnt++;
}
int fin(int x){
    if(Sfa[x]==x) return x;
    int tmp=fin(Sfa[x]);
    if(dfn[semi[val[Sfa[x]]]]<dfn[semi[val[x]]]) val[x]=val[Sfa[x]];
    return Sfa[x]=tmp;
}
void dfs(int u){
    dfn[u]=++tot; id[tot]=u;
    for(int i=head[u];~i;i=nex[i]){
        int v=to[i];
        pre[v].push_back(u);
        //把所有和这个点相连的点都加入了pre里
        if(!dfn[v]){
            fa[v]=u;
            dfs(v);
        }
    }
}
int smin(int x,int y){
    return dfn[x]<dfn[y]?x:y;
}
void solve(){
    for(int i=tot;i>=2;i--){
        int u=id[i];
        if(!pre[u].empty()){
            for(int j=0;j<pre[u].size();j++){
                int v=pre[u][j];
                //如果< 则表示为祖先 找小的就好了
                if(dfn[v]<dfn[u]) semi[u]=smin(semi[u],v);
                else{
                //否则则是后代 先进行后代中最小dfn的semi更新
                //用并查集来进行维护 再看哪个semi小来决定是否更新
                    fin(v);
                    semi[u]=smin(semi[u],semi[val[v]]);
                }
            }
        }
        Sfa[u]=fa[u]; dom[semi[u]].push_back(u);
        if(!dom[fa[u]].empty()){
            for(int j=0;j<dom[fa[u]].size();j++){
                int v=dom[fa[u]][j]; fin(v);
                int aim=val[v];
                idom[v]=(dfn[semi[aim]]<dfn[semi[v]])?aim:fa[u];
            }
        }
    }
    for(int i=2;i<=tot;i++){
        int x=id[i];
        if(idom[x]!=semi[x]) idom[x]=idom[idom[x]];
    }
}
ll ans[maxn];
void Cal(){
    rep(i,1,n) ans[i]=0;
    rep(i,1,tot){
        int x=id[i];
        ans[x]+=x;
        if(idom[x]) ans[x]+=ans[idom[x]];
     }
}
int main(){
    while(~scanf("%d%d",&n,&m)){
        rep(i,1,n){
            head[i]=-1; Sfa[i]=val[i]=semi[i]=i;
            dfn[i]=idom[i]=0;
            pre[i].clear(); dom[i].clear();
        }
        cnt=0; tot=0;
        rep(i,1,m){
            int x,y; scanf("%d%d",&x,&y);
            add(x,y);
        }
        dfs(n);
        solve();
        Cal();
        for(int i=1;i<=n;i++){
            if(i!=1) printf(" ");
            printf("%d",ans[i]);
        }
        printf("\n");
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值