• hdu 6604(支配树)


    传送门

    题意:

    给出一个$DAG$,每次询问两个点,求有多少方案通过删除一个点,使得其中一个点无法到达所有他所在的联通块出度为$0$的点。

    分析:

    考虑将图反向建边。因为可能有完全独立的两个$DAG$,因此我们考虑用一个$0$号点作为超级源点,将若干个$DAG$连成一块。至此,题目就转化成在这样的图中,某两个点$u$和$v$,有多少种删除一个点的方案,使得超级源点不能到达它们其中一个点。我们考虑从超级源点到$u$、$v$的过程,我们发现,在超级源点到$u$或$v$的路径上必定会有一些必须要经过的点。只要我们把这些路径上的必经点删掉,那么必定会贡献答案。故此时,我们只需要求出超级源点到点$u$、$v$的必经点的个数即可。

    对于这个问题,我们可以用支配树进行解决。根据支配树的性质,在支配树上,任意两点$u$、$v$之间的必经点的个数为它们之间链的长度。因此我们只需要将支配树建出来,最后统计一下超级源点到$u$、$v$两点到深度$depth$,最后再去除一下这两个点的公共祖先$lca(a.b)$的贡献即可。

    在这个题中,鉴于DAG的特殊性,因此我们可以对之前反向的图进行拓扑排序,在拓扑排序的过程中,如果发现某个节点$u$有多个父亲结点,那么我们只需要取这多个父亲节点中所有结点的$lca$即为支配树上$u$的父亲。

    整体看来,在建树过程以及询问过程均只涉及$lca$查询,故整体时间复杂度为$mathcal(nlogn+qlogn)$

    代码:

    #include <bits/stdc++.h>
    #define maxn 200005
    #define LOG 20
    using namespace std;
    struct Node{
        int to,next;
    }q1[maxn],q2[maxn],q3[maxn];
    int head1[maxn],head2[maxn],head3[maxn],cnt1,cnt2,cnt3;
    int dep[maxn],anc[maxn][LOG];
    int inde[maxn];
    vector<int>edge;
    void add_edge(int from,int to,int *head,Node *q,int &cnt){
        q[cnt].to=to;
        q[cnt].next=head[from];
        head[from]=cnt++;
    }
    void init(int n,int *head,int &cnt){
        for(int i=0;i<=n;i++) head[i]=-1,dep[i]=0,inde[i]=0;
        cnt=0;
        edge.clear();
    }
    void swim(int &x,int h){
        for(int i=0;h>0;i++){
            if(h&1)
                x=anc[x][i];
            h>>=1;
        }
    }
    int lca(int x,int y){
        if(dep[x]<dep[y]) swap(x,y);
        swim(x,dep[x]-dep[y]);
        if(x==y) return x;
        for(int i=LOG-1;i>=0;i--){
            if(anc[x][i]!=anc[y][i]){
                x=anc[x][i];
                y=anc[y][i];
            }
        }
        return anc[x][0];
    }
    void bfs(int n){
        queue<int>que;
        for(int i=1;i<=n;i++){
            if(!inde[i]){
                que.push(i);
                add_edge(0,i,head2,q2,cnt2);
                add_edge(i,0,head1,q1,cnt1);
                edge.push_back(i);
            }
        }
        while(!que.empty()){
            int x=que.front();
            que.pop();
            for(int i=head2[x];i!=-1;i=q2[i].next){
                int to=q2[i].to;
                inde[to]--;
                if(inde[to]==0) que.push(to),edge.push_back(to);
            }
        }
    }
    void BuildTree(){
        for(auto x:edge){
            int fa=-1;
            for(int i=head1[x];i!=-1;i=q1[i].next){
                int to=q1[i].to;
                if(fa==-1) fa=to;
                else fa=lca(fa,to);
            } fa= fa==-1?0:fa;
            dep[x]=dep[fa]+1;
            anc[x][0]=fa;
            for(int i=1;i<=LOG-1;i++)
                anc[x][i]=anc[anc[x][i-1]][i-1];
            add_edge(fa,x,head3,q3,cnt3);
        }
    }
    int main()
    {
        //freopen("in","r",stdin);
        int t;
        scanf("%d",&t);
        while(t--){
            int n,m;
            scanf("%d%d",&n,&m);
            init(n,head1,cnt1);
            init(n,head2,cnt2);
            init(n,head3,cnt3);
            for(int i=1;i<=m;i++){
                int from,to;
                scanf("%d%d",&from,&to);
                add_edge(from,to,head1,q1,cnt1);
                add_edge(to,from,head2,q2,cnt2);
                inde[from]++;
            }
            bfs(n);
            BuildTree();
            int q;
            scanf("%d",&q);
            while(q--){
                int from,to;
                scanf("%d%d",&from,&to);
                printf("%d
    ",dep[from]+dep[to]-dep[lca(from,to)]);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    OpenCV3入门(八)图像边缘检测
    OpenCV3入门(七)图像形态学
    OpenCV3入门(六)图像滤波
    OpenCV3入门(五)图像的阈值
    OpenCV3入门(四)图像的基础操作
    OpenCV3入门(三)基本绘图函数
    OpenCV3入门(二)Mat操作
    OpenCV3入门(一)环境搭建与实验
    图像边缘检测
    图像增强之空间域锐化
  • 原文地址:https://www.cnblogs.com/Chen-Jr/p/11267359.html
Copyright © 2020-2023  润新知