灭绝树支配树

问题模型:

在一张捕食图上(从捕食者向被捕食者有连边),若某生物的所有食物都灭绝了,则该生物灭绝。
灭绝树便是此图的一种生成树,使得满足灭绝树上某点灭绝,该点子树内所有点都灭绝

支配树,就是满足树上一个点xx的所有祖先都是它的支配点的树。

一、树形图

树形图就是自己的支配树

二、DAG

对于DAG,先拓扑排序,再建树,建树的时候顺便把倍增数组处理一下,最后dfs统计一下。
建树过程:假设i的食物有x[0]…x[k](x[0]…x[k]在拓扑排序中比i靠前),
很显然,只有x[0]~x[k]在树上的公共祖先的灭绝会导致i灭绝,否则i一定可以找到能让它活下来的食物。
于是,我们可以把i挂在x[0]~x[k]的最近公共祖先下面。
处理完所有的生物,我们得到的树就是整个图的灭绝树了。之后dfs计算一下每个节点对应的子树大小,子树大小-1即危险程度

#include <bits/stdc++.h>
#define sz 31
using namespace std;
const int maxn = 65540;
const int maxm = 1e6+100;
int cnt,nxt[maxm<<1],fst[maxn],to[maxm<<1];
int cnt1,nxt1[maxm<<1],fst1[maxn],to1[maxm<<1];
int cnt2,nxt2[maxm<<1],fst2[maxn],to2[maxm<<1];
int n,in[maxn],ran[maxn],h[maxn],f[maxn][sz],ans[maxn];
void addedge(int u,int v){
    to[++cnt] = v;
    nxt[cnt] = fst[u];
    fst[u] = cnt;
}
void addedge1(int u,int v){
    to1[++cnt1] = v;
    nxt1[cnt1] = fst1[u];
    fst1[u] = cnt1;
}
void addedge2(int u,int v){
    to2[++cnt2] = v;
    nxt2[cnt2] = fst2[u];
    fst2[u] = cnt2;
}
void topsort(){
    queue <int> q;
    for (int i=1;i<=n;i++)
        if (!in[i]) q.push(i);
    int num = 0;
    while(!q.empty()){
        int now = q.front();
        q.pop();
        ran[++num] = now;
        for (int i = fst[now];i;i=nxt[i]){
            in[to[i]]--;
            if(!in[to[i]]) q.push(to[i]);
        }
    }
}
int lca(int x,int y){
    if (h[x]<h[y]) swap(x,y);
    int k = h[x] - h[y];
    for (int i = 0;i<sz;i++)
        if ((k>>i)&1) x=f[x][i];
    if(x == y)return x;
    for (int i=sz-1;i>=0;i--)
        if (f[x][i] != f[y][i])
            x = f[x][i],y = f[y][i];
    return f[x][0];
}
void dfs(int now){
    for (int i=fst2[now];i;i=nxt2[i]){
        dfs(to2[i]);
        ans[now] += ans[to2[i]];
    }
    ans[now]++;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n;
    for (int i=1;i<=n;i++){//addedge是反的
        int x;
        cin >> x;
        while (x) {
            in[i]++;
            addedge(x,i);
            addedge1(i,x);
            cin >> x;
        }
    }
    topsort();
    for (int i =1;i<=n;i++){
        int x = to1[fst1[ran[i]]];
        for (int j = fst1[ran[i]];j;j = nxt1[j])
            x = lca(x,to1[j]);
        //the LCA of every suffix node of ran[i]
        addedge2(x,ran[i]);
        h[ran[i]] = h[x] +1;
        f[ran[i]][0] = x;
        for (int j=1;j<sz;j++)
            f[ran[i]][j] = f[f[ran[i]][j-1]][j-1];
    }

    dfs(0);
    for (int i=1;i<=n;i++)
        cout << ans[i]-1 <<endl;
}

三、一般图
L e n g a u e r − T a r j a n Lengauer−Tarjan LengauerTarjan算法
洛谷板子题解

#include <bits/stdc++.h>
#define N 500008
#define IL inline
#define M 1008611
#define R register
using namespace std;
template<typename T>void read(T &a)
{
    T x=0,f=1;char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')f=0;ch=getchar();
    }
    while(isdigit(ch))
    {
        x=(x<<1)+(x<<3)+ch-'0';ch=getchar();
    }
    a=f?x:-x;
}
/*-------------OI使我快乐-------------*/
int n,m,tot,cnt;
int head[M<<1],pre[N<<1],to[N<<2],nex[N<<2],lat[M],cdy[M];
int bel[M],val[M],sdom[M],idom[M],ans[M];
int dfn[M],id[M],fa[M];
IL void add(int *h,int x,int y)
{to[++tot]=y;nex[tot]=h[x];h[x]=tot;}
IL void dfs(int now)
{
    dfn[now]=++cnt;id[cnt]=now;
    for(R int i=head[now];i;i=nex[i])
    {
        int v=to[i];
//      printf("%d --> %d\n",now,v);
        if(dfn[v]) continue;
        dfs(v);fa[v]=now;
    }
}
IL int find(int x)
{
    if(x==bel[x]) return x;
    int root=find(bel[x]);
    if(dfn[sdom[val[bel[x]]]]<dfn[sdom[val[x]]])
        val[x]=val[bel[x]];
    return bel[x]=root;
}
IL void Tarjan()
{
//  for(R int i=1;i<=n;++i) printf("dfn[%d] %d\n",i,dfn[i]);
    for(R int i=cnt;i>=2;--i)
    {
        int now=id[i];
        //我们按照dfs序 从大到小处理 节点
        for(R int j=pre[now];j;j=nex[j])
        {//这里用链式前向星 存入度节点
            int v=to[j];
            if(!dfn[v]) continue;
            find(v);
            //dfn[y]<dfn[x]的话
            //我们还未处理到 sdom[y]=val[y]=y
            //此时y的并查集树中只有TA自己 更新是合法的

            //dfn[y]>dfn[x]的话
            //就按照套路比较
            if(dfn[sdom[val[v]]]<dfn[sdom[now]])
                sdom[now]=sdom[val[v]];
        }
        add(lat,sdom[now],now);
        bel[now]=fa[now];
        //同其在dfs树上的父亲连边
        now=fa[now];
        //现在父亲到TA这棵子树已经处理完了
        //所以此时我们可以对
        //以父亲为sdom[x]的x分别求一次sdom  然后清空
        //对于每一个点进行更新
        for(R int j=lat[now];j;j=nex[j])
        {
            int v=to[j];
            find(v);
            if(sdom[val[v]]==now) idom[v]=now;
                //如果sdom[z]==sdom[x] 那么idom[x]=sdom[x]
                //此时sdom[x]=now
            else idom[v]=val[v];
            //否则就是idom[x]=idom[z]
            //但是实际实现我们可以写成idom[x]=z
            //然后在接下来处理
        }
    }
    for(R int i=2,now;i<=cnt;++i)
    {
        now=id[i];
        if(idom[now]!=sdom[now]) idom[now]=idom[idom[now]];
        //这是残余节点的填坑大作战
    }
}
IL void dfs_ans(int now)
{
    ans[now]=1;
    for(R int i=cdy[now];i;i=nex[i])
    {
        int v=to[i];
        dfs_ans(v);
        ans[now]+=ans[v];
    }
}
int main()
{
    read(n);read(m);
    for(R int i=1,x,y;i<=m;++i)
    {
        read(x);read(y);
        add(head,x,y);add(pre,y,x);
    }
    for(R int i=1;i<=n;++i)
        sdom[i]=bel[i]=val[i]=i;
    dfs(1);
    Tarjan();
//  puts("How old are you ? ");
//  for(int i=1;i<=n;++i)
//    {
//      printf("支配点[%d]   %d\n",i,idom[i]);
//    }
    tot=0;
    for(R int i=2;i<=n;++i) if(idom[i]) add(cdy,idom[i],i);
    dfs_ans(1);
    for(R int i=1;i<=n;++i) printf("%d%c",ans[i],(i==n ? '\n':' '));
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值