hdu 5420 Victor and Proposition 线段树建图+强连通分量

题意:
http://bestcoder.hdu.edu.cn/contests/contest_chineseproblem.php?cid=620&pid=1003

题目求有多少对互为充要条件,很容易看出是直接求强连通分量然后分量再计算就可得出。。但是没想到怎么建图,如果暴力直接建图复杂度达到 O(n2) 这样会TLE的。看了题解后觉得服气了。。对于每个节点记录其子树深度为i的节点,然后新增节点,深度高的向深度低的连边,每个节点连向代表它的真实节点。
代码:

//author: CHC
//First Edit Time: 2015-08-23 19:58
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <set>
#include <algorithm>
#include <limits>
using namespace std;
typedef long long LL;
const int MAXN=100000+1000;
const int MAXM=MAXN*20;
const int INF = numeric_limits<int>::max();
const LL LL_INF= numeric_limits<LL>::max();
struct Edge {
    int to,next;
    Edge(){}
    Edge(int _to,int _next):to(_to),next(_next){}
}e[MAXM<<3];
int head[MAXM],tot;
void init(){
    memset(head,-1,sizeof(head));
    tot=0;
}
void AddEdge(int u,int v){
    e[tot]=Edge(v,head[u]);
    head[u]=tot++;
}
typedef pair<int,int> ii;
vector <ii> t[MAXN<<2];
int d[MAXN],L[MAXN],R[MAXN],dep[MAXN],ncnt,rid[MAXN],n,Begin[MAXN<<2];
void dfs(int u,int d){
    L[u]=++ncnt;
    rid[ncnt]=u;
    dep[u]=d;
    for(int i=head[u];~i;i=e[i].next)
        dfs(e[i].to,d+1);
    R[u]=ncnt;
}
#define lson L,mid,rt<<1
#define rson mid+1,R,rt<<1|1
void build(int L,int R,int rt){
    t[rt].resize(R-L+1);
    if(L==R){
        Begin[rt]=n+1;
        t[rt][0]=make_pair(dep[rid[L]],rid[L]);
        ++n;
        AddEdge(n,rid[L]);
        return ;
    }
    int mid=(L+R)>>1;
    build(lson);build(rson);
    merge(t[rt<<1].begin(),t[rt<<1].end(),t[rt<<1|1].begin(),t[rt<<1|1].end(),t[rt].begin());
    Begin[rt]=n+1;
    for(int i=1;i<R-L+1;i++)AddEdge(n+1+i,n+i);
    for(int i=0;i<R-L+1;i++)AddEdge(n+1+i,t[rt][i].second);
    n+=R-L+1;
}
void connect(int L,int R,int rt,int l,int r,int x,int d){
    if(l<=L&&R<=r){
        int pos=lower_bound(t[rt].begin(),t[rt].end(),make_pair(d,1<<30))-t[rt].begin()-1;
        if(~pos)AddEdge(x,Begin[rt]+pos);
        return ;
    }
    int mid=(L+R)>>1;
    if(l<=mid)connect(lson,l,r,x,d);
    if(r>mid)connect(rson,l,r,x,d);
}
int dfn[MAXM],low[MAXM],sta[MAXM],top,times,bleg[MAXM],cnt[MAXM];
void tarjan(int u){
    low[u]=dfn[u]=++times;
    sta[++top]=u;
    for(int i=head[u];~i;i=e[i].next){
        int v=e[i].to;
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!bleg[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u]){
        bleg[0]++;
        cnt[bleg[0]]=0;
        do{
            bleg[sta[top]]=bleg[0];
            cnt[bleg[0]]+=(sta[top]<=ncnt);
        }while(sta[top--]!=u);
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        init();
        for(int i=2,x;i<=n;i++){
            scanf("%d",&x);
            AddEdge(x,i);
        }
        ncnt=0;
        dfs(1,0);
        init();
        build(1,ncnt,1);
        for(int i=1,x,y;i<=ncnt;i++){
            scanf("%d%d",&x,&y);
            connect(1,ncnt,1,L[x],R[x],i,dep[x]+y);
        }
        memset(dfn,0,sizeof(dfn));
        memset(bleg,0,sizeof(bleg));
        top=times=0;
        for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
        LL ans=0;
        for(int i=1;i<=bleg[0];i++){
            //printf("%d ",cnt[i]);
            ans+=(LL)cnt[i]*(cnt[i]-1)/2;
        }
        //puts("");
        printf("%I64d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(线段树,强连通分量,建图)