CodeChef Graphcnt:Counting on a directed graph(支配树)

传送门

题意:
给一张图,求有多少个无序点对 (x,y) ( x , y ) 满足 存 在 一 条 1 x1 y1$这一个公共点。

题解:
支配树
求出必经点之后就是支配树上lca为1的点对数量了。

#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;
}
const int N=1e5+50,M=1e6+50;
int n,m;
long long ans;
int dfn[N],id[N],fa[N],sze[N],ind;
int anc[N],mnsd[N],sdom[N],idom[N];
vector<int>pre[N];
vector<int>buc[N];
struct Graph*tr;
struct Graph{
    int g[N],nt[M],v[M],ec;
    inline void adde(int x,int y){
        nt[++ec]=g[x];g[x]=ec;v[ec]=y;
    }
    inline void dfs(int x){
        sze[x]=1;
        for(int e=g[x];e;e=nt[e]){
            dfs(v[e]);
            if(x==1)ans+=1ll*sze[x]*sze[v[e]];
            sze[x]+=sze[v[e]];
        }
    }
    inline void add(int x,int y){
        nt[++ec]=g[x];g[x]=ec;v[ec]=y;
        pre[y].push_back(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){
        getanc(p);
        return mnsd[p];
    }
    inline void dfs(int x,int f){
        dfn[x]=++ind;id[ind]=x;fa[x]=f;
        mnsd[x]=(anc[x]=(sdom[x]=x));
        for(int e=g[x];e;e=nt[e]){
            if(dfn[v[e]])continue;
            dfs(v[e],x);
        }
    }
    inline void build(){
        for(int i=ind;i>=2;i--){
            int u=id[i];
            for(int p=pre[u].size()-1;p>=0;p--){
                int x=pre[u][p],t=eval(x);
                if(dfn[sdom[t]]<dfn[sdom[u]])sdom[u]=sdom[t];
            }
            int f=fa[u];
            anc[u]=f;
            buc[sdom[u]].push_back(u);
            for(int p=buc[f].size()-1;p>=0;p--){
                int x=buc[f][p],t=eval(x);
                if(dfn[sdom[x]]>dfn[sdom[t]])idom[x]=t;
                else idom[x]=sdom[x];
            }
            buc[f].clear();
        }
        for(int i=2;i<=ind;i++){
            int u=id[i];
            if(idom[u]!=sdom[u])idom[u]=idom[idom[u]];
            tr->adde(idom[u],u);
        }
    }
}ori,g;

int main(){
    tr=&g;
    n=rd(),m=rd();
    for(int i=1;i<=m;i++){
        int x=rd(),y=rd();
        ori.add(x,y);
    }
    ori.dfs(1,1);
    ori.build();
    tr->dfs(1);
    cout<<ans<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值