hdu 5420 Victor and Proposition(强连通+线段树建图)

来源:互联网 发布:选择网络项目的标准 编辑:程序博客网 时间:2024/05/23 02:03

题意:给出一棵树,每个点都给一个x,d,代表它和x这个点以及以下深度差小于d的点都有一条有向边,求互相可达的u,v对数。

做法:可以很容易发觉如果我们把点连好然后直接找强连通分量,然后答案就是每个强连通分量大小x*(x-1)/2之和。

但是因为点有10000个,边极限可以出到n^2个的。所以我们得简化边的个数。先处理出dfs序,保存一下每个点子树的dfs序范围,可以发现,x以及它之下深度小于d的点其实在x子树的dfs序范围里的,我们如果能把这些点按照深度排序,自然就能得到连续的想要的点,基于这个思想。我们考虑对dfs序建立线段树。区间[l,r]的vector代表的dfs序标号为l,r范围内的点,这些点是个二元组,除了是谁还得保存下深度,按之前所说我们把它们按照深度排序,我们把这些排序后的点建新的点来建图类似这样 1<—2 <— 3 <—4,再把这些新的点与原来它代表的点连边,这样的话,如果我想要的d范围最大到3(由于排序了,可以二分找),那么我就跟3连一条边,这样就小于d的点也都连上了。

至于限制条件是必须在x的子树dfs序范围内这个问题,我们可以通过类似线段树区间更新的方式来解决。由于有了线段树,任何dfs区间一定是由不超过logn个区间组合得到,所以就按照刚才的方式递归建边即可。

最后再来算一下空间和时间。我们要新建的点数等于线段树中每个vector的size和。假设某一层为i,那么它要存的点个数是2^(dep-i)个(dep是最大深度),这一层点的个数是2^i个,两者相乘就是2^dep个,即n个,也就是说每一层需要n的空间,树高为logn,所以点数就是o(nlogn)的,边数的话,由于我们需要把这些点之间连边,以及每个新建的点要和原来连边,所以还要乘一个2。

由于子树每个区间都是有序的,我们可以用归并排序,所以建树复杂度o(nlogn),tarjan复杂度o(n+m) = o(n+nlogn)所以总复杂度o(nlogn)。

PS:由于本弱是仔细研读了昂神的代码才理解的。所以写的比较像来着。另。merge竟然比我手写的归并要快。这是为啥。。

AC代码:

#pragma comment(linker, "/STACK:102400000,102400000")#include<cstdio>#include<ctype.h>#include<algorithm>#include<iostream>#include<cstring>#include<vector>#include<cstdlib>#include<stack>#include<queue>#include<set>#include<map>#include<cmath>#include<ctime>#include<string.h>#include<string>#include<sstream>#include<bitset>using namespace std;#define ll __int64#define ull unsigned __int64#define eps 1e-8#define NMAX 1000000000#define MOD (1<<30)#define lson l,mid,rt<<1#define rson mid+1,r,rt<<1|1#define PI acos(-1)template<class T>inline void scan_d(T &ret){    char c;    int flag = 0;    ret=0;    while(((c=getchar())<'0'||c>'9')&&c!='-');    if(c == '-')    {        flag = 1;        c = getchar();    }    while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar();    if(flag) ret = -ret;}const int maxn = 100000+10;const int maxm = 100000*30+10;struct node{    int dep,pos;    node(){}    node(int _d, int _p):dep(_d),pos(_p){}    bool operator < (const node &t) const    {        if(dep != t.dep) return dep < t.dep;        return pos < t.pos;    }};int head[maxm],ecnt;struct Edge{    int v,next;}e[maxm<<1];void add_edge(int u, int v){    e[ecnt].v = v; e[ecnt].next = head[u];    head[u] = ecnt++;}int st[maxn],ed[maxn],dep[maxn],dfs_clock,n,nct;int a[maxn];void dfs(int u, int d){    st[u] = ++dfs_clock;    a[dfs_clock] = u;    dep[u] = d;    for(int i = head[u]; ~i; i = e[i].next)    {        int v = e[i].v;        dfs(v,d+1);    }    ed[u] = dfs_clock;}vector<node>v[maxn<<2];int beg[maxn<<2];void combin(vector<node> &x, vector<node> &y, vector<node> &z){    int sz1 = x.size(), sz2 = y.size();    int p = 0, p1 = 0, p2= 0;    while(p1 < sz1 || p2 < sz2)    {        if(p1 == sz1 || (p2 != sz2 && y[p2] < x[p1])) z[p++] = y[p2++];        else z[p++] = x[p1++];    }}void build(int l, int r, int rt){    v[rt].resize(r-l+1);    if(l == r)    {        v[rt][0] = node(dep[a[l]],a[l]);        n++;        add_edge(n,a[l]);        beg[rt] = n;        return;    }    int mid = l+r >> 1;    build(lson);    build(rson);//    combin(v[rt<<1],v[rt<<1|1],v[rt]);    merge(v[rt<<1].begin(),v[rt<<1].end(),v[rt<<1|1].begin(),v[rt<<1|1].end(),v[rt].begin());    beg[rt] = n+1;    for(int i = 1; i < r-l+1; i++)        add_edge(n+i+1,n+i);    for(int i = 0; i < r-l+1; i++)        add_edge(n+i+1,v[rt][i].pos);    n += r-l+1;}void update(int L, int R, int x, int d, int l, int r, int rt){    if(L <= l && R >= r)    {        int pos = lower_bound(v[rt].begin(), v[rt].end(), node(d+1,0))-v[rt].begin()-1;        if(pos >= 0) add_edge(x,beg[rt]+pos);        return;    }    int mid = l+r >> 1;    if(L <= mid) update(L,R,x,d,lson);    if(R > mid) update(L,R,x,d,rson);}int pre[maxm],lowlink[maxm],sum[maxm],scc_cnt;bool sccno[maxm];stack<int>S;void dfs(int u){    pre[u] = lowlink[u] = ++dfs_clock;    S.push(u);    for(int i = head[u]; ~i; i = e[i].next)    {        int v = e[i].v;        if(!pre[v])        {            dfs(v);            lowlink[u] = min(lowlink[u],lowlink[v]);        }        else if(!sccno[v])        {            lowlink[u] = min(lowlink[u],pre[v]);        }    }    if(lowlink[u] == pre[u])    {        ++scc_cnt;        while(1)        {            int x = S.top(); S.pop();            sccno[x] = 1;            sum[scc_cnt] += x <= nct;            if(x == u) break;        }    }}void find_scc(){    dfs_clock = scc_cnt = 0;    memset(pre,0,sizeof(pre));    memset(sccno,0,sizeof(sccno));    memset(sum,0,sizeof(sum));    for(int i = 1; i <= nct; i++) if(!pre[i])        dfs(i);    ll ans = 0;    for(int i = 1; i <= scc_cnt; i++)        ans += (ll)sum[i]*(sum[i]-1)/2;    printf("%I64d\n",ans);}int main(){#ifdef GLQ    freopen("input.txt","r",stdin);//    freopen("o.txt","w",stdout);#endif    int _t;    scanf("%d",&_t);    while(_t--)    {        scanf("%d",&n);        nct = n;        memset(head,-1,sizeof(head));        ecnt = 0;        for(int i = 2; i <= n; i++)        {            int v;            scanf("%d",&v);            add_edge(v,i);        }        dfs_clock = 0;        dfs(1,0);        memset(head,-1,sizeof(head));        ecnt = 0;        build(1,nct,1);        for(int i = 1; i <= nct; i++)        {            int x,d;            scanf("%d%d",&x,&d);            update(st[x],ed[x],i,dep[x]+d,1,nct,1);        }        find_scc();    }    return 0;}



0 0
原创粉丝点击