zoukankan      html  css  js  c++  java
  • hdu 5458 Stability(生成树,树链剖分,好题)

    题目链接

    Stability

    Time Limit: 3000/2000 MS (Java/Others) Memory Limit: 65535/102400 K (Java/Others)
    Total Submission(s): 1607 Accepted Submission(s): 396

    Problem Description
    Given an undirected connected graph G with n nodes and m edges, with possibly repeated edges and/or loops. The stability of connectedness between node u and node v is defined by the number of edges in this graph which determines the connectedness between them (once we delete this edge, node u and v would be disconnected).

    You need to maintain the graph G, support the deletions of edges (though we guarantee the graph would always be connected), and answer the query of stability for two given nodes.

    Input
    There are multiple test cases(no more than 3 cases), and the first line contains an integer t, meaning the totally number of test cases.

    For each test case, the first line contains three integers n, m and q, where 1≤n≤3×104,1≤m≤105 and 1≤q≤105. The nodes in graph G are labelled from 1 to n.

    Each of the following m lines contains two integers u and v describing an undirected edge between node u and node v.

    Following q lines - each line describes an operation or a query in the formats:
    ⋅ 1 a b: delete one edge between a and b. We guarantee the existence of such edge.
    ⋅ 2 a b: query the stability between a and b.

    Output
    For each test case, you should print first the identifier of the test case.

    Then for each query, print one line containing the stability between corresponding pair of nodes.

    Sample Input
    1
    10 12 14
    1 2
    1 3
    2 4
    2 5
    3 6
    4 7
    4 8
    5 8
    6 10
    7 9
    8 9
    8 10
    2 7 9
    2 7 10
    2 10 6
    2 10 5
    1 10 6
    2 10 1
    2 10 6
    2 3 10
    1 8 5
    2 5 10
    2 4 5
    1 7 9
    2 7 9
    2 10 5

    Sample Output
    Case #1:
    0
    0
    0
    0
    2
    4
    3
    3
    2
    3
    4

    Source
    2015 ACM/ICPC Asia Regional Shenyang Online

    题意:

    给出一个 (n) 个结点 (m) 条边的无向图(保证连通),可能有重边和自环,现在给出 (q) 组询问:

    1. $ 1,u,v $ 删除 (u,v) 之间的一条边。
    2. (2,u,v) 询问结点 (u) 到结点 (v) 的路径上有多少关键边,关键边就是指 (u)(v) 的所有路径必须经过的边

    保证在删边过程中图一直连通。

    题解:

    可以逆着来,首先得到所有操作完后的图,对这个图求一棵生成树,然后将生成树上每一条边的权值置为1,再树链剖分,然后一条条加边(此时必定会形成环),对于加的边 ((u,v)),将 (u->v) 路径上的边置为1,对于询问 ((u,v)) ,答案为 (u->v) 的边上权值之和。

    代码里使用了claris求三元环那个代码里面hash加边法。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<queue>
    #include<stack>
    #include<set>
    using namespace std;
    #define rep(i,a,n) for (int i=a;i<n;i++)
    #define per(i,a,n) for (int i=n-1;i>=a;i--)
    #define pb push_back
    #define fi first
    #define se second
    #define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<endl;
    typedef vector<int> VI;
    typedef long long ll;
    typedef pair<int,int> PII;
    const int maxn=3e4+100;
    const int maxm=1e5+100;
    const int Base=(1<<20)-1;
    int head[maxn];
    struct edge
    {
        int to,next;
    }e[maxn*2];
    int tol=0;
    void add(int u,int v)
    {
        e[++tol].to=v,e[tol].next=head[u],head[u]=tol;
    }
    
    int G[Base+1];
    struct edge1
    {
        int from,to,id,next;
    }e1[maxm];
    int tol1=0;
    void add1(int u,int v,int id)
    {
        int h=((u<<8)|v)&Base;
        e1[++tol1].to=v,e1[tol1].from=u,e1[tol1].id=id,e1[tol1].next=G[h],G[h]=tol1;
    }
    
    pair<pair<int,int>,int> e2[maxm];
    struct que
    {
        int u,v,op;
    }query[maxm];
    
    int check(int u,int v)
    {
        int h=((u<<8)|v)&Base;
        for(int i=G[h];i;i=e1[i].next)
        {
            int u1=e1[i].from,v1=e1[i].to;
            if(u1==u&&v1==v&&e2[e1[i].id].se==1)
            {
                return e1[i].id;
            }
        }
        return 0;
    }
    
    int pa[maxn];
    int find(int x)
    {
        return pa[x]==x? x:pa[x]=find(pa[x]);
    }
    set<int> st;
    
    int seg[maxn*4],lazy[maxn*4];
    void build(int i,int l,int r)
    {
        lazy[i]=0;
        if(l==r)
        {
            seg[i]=1;
            return;
        }
        int m=(l+r)/2;
        build(i*2,l,m);
        build(i*2+1,m+1,r);
        seg[i]=seg[i*2]+seg[i*2+1];
    }
    
    void update(int i,int l,int r,int L,int R)
    {
        if(l==L&&r==R)
        {
            seg[i]=0;
            lazy[i]=1;
            return;
        }
        if(lazy[i]) return;
        int m=(L+R)/2;
        if(r<=m) update(i*2,l,r,L,m);
        else if(l>m) update(i*2+1,l,r,m+1,R);
        else
        {
            update(i*2,l,m,L,m);
            update(i*2+1,m+1,r,m+1,R);
        }
        seg[i]=seg[i*2]+seg[i*2+1];
    }
    
    int ask(int i,int l,int r,int L,int R)
    {
        if(l==L&&r==R)
        {
            return seg[i];
        }
        if(lazy[i]) return 0;
        int m=(L+R)/2;
        if(r<=m) return ask(i*2,l,r,L,m);
        else if(l>m) return ask(i*2+1,l,r,m+1,R);
        else
        {
            return ask(i*2,l,m,L,m)+ask(i*2+1,m+1,r,m+1,R);
        }
    }
    
    int pos;//线段树总区间大小
    int fa[maxn],top[maxn],son[maxn],num[maxn],p[maxn],dep[maxn];
    //top[v]表示v所在的重链的顶端节点,对于u的轻儿子v有top[v]=v,fa[v]表示v的父亲节点,num[v]表示以v为根的子树的节点数,p[v]表示v与其父亲节点的连边在线段树中的位置,son[v]为v的重儿子,dep为深度
    void dfs(int u,int f,int d)
    {
        num[u]=1;
        fa[u]=f;
        dep[u]=d;
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(v==f) continue;
            dfs(v,u,d+1);
            if(son[u]==0||num[v]>num[son[u]])
                son[u]=v;
            num[u]+=num[v];
        }
    }
    void dfs2(int u,int sp)
    {
        top[u]=sp;
        if(son[u])
        {
            p[u]=pos++;
            dfs2(son[u],sp);
        }
        else    //叶子结点
        {
            p[u]=pos++;
            return;
        }
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(v!=son[u]&&v!=fa[u])
                dfs2(v,v);
        }
    }
    void init(int n)
    {
        pos=1;
        rep(i,1,n+1) fa[i]=son[i]=0;
    }
    int solve(int u,int v,int n)  //查询u-v之间的最大边
    {
        int f1=top[u],f2=top[v];
        int ans=0;
        while(f1!=f2)
        {
            if(dep[f1]<dep[f2])
            {
                swap(f1,f2);
                swap(u,v);
            }
            ans+=ask(1,p[f1],p[u],1,n);
            u=fa[f1],f1=top[u];
        }
        if(u==v) return ans;  //注意此处的操作对象是树上的边,当是点的时候此行应该注释掉!
        if(dep[u]>dep[v]) swap(u,v);
        return ans+ask(1,p[son[u]],p[v],1,n);
    }
    
    void solve1(int u,int v,int n)  //查询u-v之间的最大边
    {
        int f1=top[u],f2=top[v];
        while(f1!=f2)
        {
            if(dep[f1]<dep[f2])
            {
                swap(f1,f2);
                swap(u,v);
            }
            update(1,p[f1],p[u],1,n);
            u=fa[f1],f1=top[u];
        }
        if(u==v) return;  //注意此处的操作对象是树上的边,当是点的时候此行应该注释掉!
        if(dep[u]>dep[v]) swap(u,v);
        update(1,p[son[u]],p[v],1,n);
    }
    int ans[maxm];
    int main()
    {
        int cas;
        scanf("%d",&cas);
        int t=0;
        while(cas--)
        {
            st.clear();
            tol=tol1=0;
            int n,m,q;
            scanf("%d%d%d",&n,&m,&q);
            init(n);
            rep(i,1,n+1) head[i]=0,pa[i]=i;
            rep(i,0,Base+1) G[i]=0;
            
            rep(i,1,m+1)
            {
                int u,v;
                scanf("%d%d",&u,&v);
                if(u>v) swap(u,v);
                add1(u,v,i);
                e2[i]=make_pair(make_pair(u,v),1);
            }
            rep(i,1,q+1)
            {
                scanf("%d%d%d",&query[i].op,&query[i].u,&query[i].v);
                if(query[i].u>query[i].v) swap(query[i].u,query[i].v);
                if(query[i].op==1)
                {
                    int id=check(query[i].u,query[i].v);
                    e2[id].se=0;
                }
            }
            rep(i,1,m+1)
            {
                if(e2[i].se)
                {
                    int u=e2[i].fi.fi,v=e2[i].fi.se;
                    int fu=find(u),fv=find(v);
                    if(fu!=fv)
                    {
                        pa[fu]=fv;
                        add(u,v);
                    }
                    else
                    {
                        st.insert(i);
                    }
                }
            }
            dfs(1,0,1);
            dfs2(1,1);
            build(1,1,n);
            for(auto item:st)
            {
                int u=e2[item].fi.fi,v=e2[item].fi.se;
                solve1(u,v,n);
            }
            
            per(i,1,q+1)
            {
                if(query[i].op==1)
                {
                    solve1(query[i].u,query[i].v,n);
                }
                else if(query[i].op==2)
                {
                    ans[i]=solve(query[i].u,query[i].v,n);
                }
            }
            printf("Case #%d:
    ",++t);
            rep(i,1,q+1) if(query[i].op==2) printf("%d
    ",ans[i]);
        }
        return 0;
    }
    
  • 相关阅读:
    Java多线程之JUC包:ReentrantLock源码学习笔记
    Java多线程之JUC包:Semaphore源码学习笔记
    Java多线程之JUC包:CountDownLatch源码学习笔记
    Java多线程之JUC包:AbstractQueuedSynchronizer(AQS)源码学习笔记
    Java多线程之JUC包:CyclicBarrier源码学习笔记
    架构设计:企业总体架构要如何做?小白也能快速领悟的设计思想
    可参考才是有价值的,架构设计的技改之路从来都不容易
    架构设计:高并发读取,高并发写入,并发设计规划落地方案思考
    微服务手册:API接口9个生命节点,构建全生命周期管理
    百度api使用心得体会
  • 原文地址:https://www.cnblogs.com/tarjan/p/7569433.html
Copyright © 2011-2022 走看看