zoukankan      html  css  js  c++  java
  • D1 HL 图的高级应用 tarjan算法

    临时跑去D班取听神仙课,不会的还是好多(我真菜);

    1.tarjan算法;

    受欢迎的牛

    tarjan求强连通分量,进行缩点,求缩点后出度为0的点,如果存在两个及以上的点出度都为零 那么输出无解;

    否则输出这个强联通分量的大小;

    #include<bits/stdc++.h>
    #define N 100500
    using namespace std;
    template<typename T>inline void read(T &x)
    {
        x=0;
        register int f=1;
        register char ch=getchar();
        while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    struct gg
    {
        int next,y;
    }e[N<<1];
    int lin[N],dfn[N],low[N],cnt,tot,hl,n,m;
    int du[N],id[N],all[N];
    bool v[N];
    stack<int>s;
    inline void add(int x,int y)
    {
        cnt++;
        e[cnt].y=y;
        e[cnt].next=lin[x];
        lin[x]=cnt;
    }
    void tarjan(int x)
    {
        dfn[x]=low[x]=++tot;
        s.push(x);v[x]=true;
        for(int i=lin[x];i;i=e[i].next)
        {
            int u=e[i].y;
            if(!dfn[u])
            {
                tarjan(u);
                low[x]=min(low[x],low[u]);
            }
            else if(v[u])low[x]=min(low[x],dfn[u]);
        }
        int k;
        if(low[x]==dfn[x])
        {
            ++hl;
            do
            {
                k=s.top();s.pop();
                v[k]=false;
                id[k]=hl;all[hl]++;
            }while(x!=k);
        }
    }
    int main()
    {
        read(n);read(m);
        int a,b;
        for(int i=1;i<=m;i++)
        {
            read(a);read(b);
            add(a,b);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i])    tarjan(i);
            for(int k=1;k<=n;k++)
            {
                for(int i=lin[k];i;i=e[i].next)
                {
                    int u=e[i].y;
                    if(id[k]!=id[u])
                        du[id[k]]++;
                }
            }
        int t=0;
        for(int i=1;i<=hl;i++)
            if(!du[i])
            {
                if(t)
                {    
                    puts("0");
                    return 0;
                }
                t=i;
            }
        printf("%d
    ",all[t]);
        return 0;
    }
    View Code

    2.杀人游戏

    tarjan求强连通分量,对于一个强联通的分量,知道其中一个人就知道这个块里的其他人;

    细节:考虑一种情况,求完tarjan后缩点,存在一个入度为0的点,它的强联通分量大小为1,他所连的点的入度>1,我们是不需要知道这个点的;

    因为他一定能被其他n-1个点确定后推出;如果等于1,那么我们是必须知道这个入度为0的点的身份的;

    #include<bits/stdc++.h>
    #define N 500500
    using namespace std;
    template<typename T>inline void read(T &x)
    {
        x=0;
        register int f=1;
        register char ch=getchar();
        while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    struct pink
    {
        int x,y;
    }a[N<<1];
    struct gg
    {
        int next,y;
    }e[N<<1];
    int lin[N<<1],dfn[N],low[N],cnt,tot,hl,n,m,ans[N];
    int in[N],id[N],all[N],flag;
    bool v[N];
    stack<int>s;
    inline void add(int x,int y)
    {
        cnt++;
        e[cnt].y=y;
        e[cnt].next=lin[x];
        lin[x]=cnt;
    }
    void tarjan(int x)
    {
        dfn[x]=low[x]=++tot;
        s.push(x);v[x]=true;
        for(int i=lin[x];i;i=e[i].next)
        {
            int u=e[i].y;
            if(!dfn[u])
            {
                tarjan(u);
                low[x]=min(low[x],low[u]);
            }
            else if(v[u])    low[x]=min(low[x],dfn[u]);
        }
        int k;
        if(low[x]==dfn[x])
        {
            ++hl;
            do
            {
                k=s.top();s.pop();
                v[k]=false;
                id[k]=hl;all[hl]++;
            }while(x!=k);
        }
    }
    int main()
    {    
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        read(n);read(m);
        for(int i=1;i<=m;i++)
        {
            read(a[i].x);read(a[i].y);
            add(a[i].x,a[i].y);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i])    tarjan(i);
        memset(lin,0,sizeof(lin));
        cnt=0;
        for(int i=1;i<=m;i++)
        {
            if(id[a[i].x]!=id[a[i].y])
            {
                in[id[a[i].y]]++;
                add(id[a[i].x],id[a[i].y]);
            }
        }
        int ans=0;
        for(int i=1;i<=hl;i++)
        {
            if(!flag&&!in[i]&&all[i]==1)
            {
                int p=0;
                for(int j=lin[i];j;j=e[j].next)
                {
                    int yy=e[j].y;
                    if(in[yy]==1) p=1;
                }
                if(!p) flag=1;
            }
            if(!in[i]) ans++;
        }
        if(flag) ans--;
        printf("%.6f
    ",1.0-(double)ans/(double)n);
        return 0;
    }
    View Code

    3.矿场搭建

    我们需要tarjan跑出所有的割点,然后dfs每一个联通块及大小,如果这个联通块内部不存在割点,那么我么至少建两个救援所,为了避免其中一个救援所炸掉,

    而且我们要明白,救援所是不能建在隔点上的,如果这个联通块中存在两个及以上割点,我们是不需要键救援所的;如果存在一个割点,建两个,设在不是割点的电上;

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=510;
    struct gg
    {
        int y,next;
    }a[maxn*maxn];
    template<typename T>inline void read(T &x)
    {
        x=0;
        T f=1,ch=getchar();
        while (!isdigit(ch)) ch=getchar();
        if (ch=='-') f=-1, ch=getchar();
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
    long long hl,ans=1;
    int lin[maxn],len;
    inline void add(int x,int y)
    {
        a[++len].y=y;
        a[len].next=lin[x];
        lin[x]=len;
    }
    int dfn[maxn],low[maxn],num,root;
    bool cut[maxn];
    inline void tarjan(int x,int fa)
    {
        dfn[x]=low[x]=++num;
        int flag=0;
        for (int i=lin[x];i;i=a[i].next)
        {
            int y=a[i].y;
            if (!dfn[y])
            {
                tarjan(y,x);
                low[x]=min(low[x],low[y]);
                if(low[y]>=dfn[x])
                {
                    ++flag;
                    if (x!=root||flag>1) cut[x]=1;
                }
            }
            else if (y!=fa)
                low[x]=min(low[x],dfn[y]);
        }
    }
    int vis[maxn],k,cnt;
    inline void dfs(int x)
    {
        vis[x]=k;
        ++cnt;
        for(int i=lin[x];i;i=a[i].next)
        {
            int y=a[i].y;
            if(vis[y]!=k&&cut[y]) ++num,vis[y]=k;
            if(!vis[y])    dfs(y);
        }
    }
    int main()
    {
        //freopen("input.in","r",stdin);
        //freopen("output.out","w",stdout);
        for (int ca=1;;++ca)
        {
            int n=0,m;
            read(m);
            if(!m) exit(0);
            memset(dfn,0,sizeof(dfn));
            memset(vis,0,sizeof(vis));
            memset(cut,0,sizeof(cut));
            memset(lin,0,sizeof(lin));
            hl=k=num=len=0;
            ans=1;
            for (int i=1;i<=m;++i)
            {
                int x,y;read(x);read(y);
                n=max(n,max(x,y));
                add(x,y);add(y,x);
            }
            for (int i=1;i<=n;++i)
                if(!dfn[i]) root=i,tarjan(i,i);
            for (int i=1;i<=n;++i)
                if(!vis[i]&&!cut[i])
                {
                    ++k,cnt=num=0;
                    dfs(i);
                    if(!num) hl+=2,ans*=cnt*(cnt-1)/2;
                    if(num==1) hl++,ans*=cnt;
                }
            printf("Case %d: %lld %lld
    ",ca,hl,ans);
        }
        return 0;
    }
    View Code

    圆方树,听懂了但没写题;

    竞赛图,强联通竞赛图;

    支配树,被支配;

    4.炸弹

    大致思路:线段树优化建边,区间连边,求tarjan强连通分量,然后反向拓扑;

    我觉得自己对线段树优化建边理解的不是特别深刻,也是第一次写;

    但是他可以将边优化成log级别的,节省很多空间和时间;

    题解:

    首先应该明确的是这道题一定是用图论知识来做,当一个炸弹i被引爆时,对于能够被当前这颗炸弹i引爆的炸弹j,我们肯定是要建一条i-->j的边,但是由于题目中的边数较多,所以不可能这样建图,那我们可以想到,当一个炸弹被引爆,那么最总一共被引爆的炸弹一定是连续的,所以就有引出区间问题了,那么区间问题我们就可以用线段树来做,当i炸弹能将(ID)【L,R】的炸弹全部引爆时,就建一条pos【i】-->ID的边,那么这样就一定会形成环,因为这颗炸弹处于区间中间,那么久tarjan缩点,然会就是求每个点能够最远到达哪个点,那么要么就dfs(有点慢),有么就top排序,如果是top排序的话,就要反向建图,从最远的点一步一步的推回去,从而解决题目。

    总结:充分利用线段树的区间和树形性质,当图论问题转换成区间问题时,我们就可以利用线段树的特性来优化时间和空间。
    原文:https://blog.csdn.net/shiyongyang/article/details/77984795

    #include<bits/stdc++.h>
    using namespace std;
    #define N 1000010
    #define LL long long 
    
    template<typename T>inline void read(T &x)
    {
        x=0;T f=1,ch=getchar();
        while(!isdigit(ch)) {if(ch=='-')  f=-1;  ch=getchar();}
        while(isdigit(ch))  {x=(x<<1)+(x<<3)+(ch^48);  ch=getchar();}
        x*=f;
    }
    
    int n,tot,tc,num,top,cnt,lin[N<<2],linc[N<<2],vis[N<<2],ins[N<<2],c[N<<2],Stack[N<<2],pos[N<<2];
    int dfn[N<<2],low[N<<2];
    LL X[N],R[N],mn[N<<2],mx[N<<2],vmin[N<<1],vmax[N<<1],rd[N<<1];
    
    struct gg {
        int x,y,next;
    }a[N<<2],e[N<<2]; 
    queue<int> q;
    inline void add(int x,int y) {
        a[++tot].y=y; a[tot].next=lin[x]; lin[x]=tot;
    }
    
    inline void build(int l,int r,int p) {
        if(l==r) {
            pos[l]=p;
            return ;
        }
        int mid=(l+r)>>1;
        mn[p]=1ll<<62,mx[p]=-1ll<<62;
        build(l,mid,p<<1);
        build(mid+1,r,p<<1|1);
        add(p,p<<1); add(p,p<<1|1);
    }
    
    inline void connect(int b,int e,int x,int l,int r,int y) {
        if(b<=l&&e>=r) {
            add(x,y);
            //cout<<b<<' '<<e<<endl;
            return ;
        }
        int mid=(l+r)>>1;
        if(b<=mid) connect(b,e,x,l,mid,y<<1);
        if(e>mid) connect(b,e,x,mid+1,r,y<<1|1);
    }
    
    void tarjan(int x)
    {
        dfn[x]=low[x]=++num,ins[x]=1,Stack[++top]=x;
        for(int i=lin[x];i;i=a[i].next)
        {
            int y=a[i].y;
            //cout<<x<<' '<<y<<endl;
            if(!dfn[y]) tarjan(y),low[x]=min(low[x],low[y]);
            else if(ins[y]) low[x]=min(low[x],dfn[y]);
        }
        if(dfn[x]==low[x])
        {
            int t;
            cnt++,vmin[cnt]=1ll<<62,vmax[cnt]=-1ll<<62;
            do
            {
                t=Stack[top--],ins[t]=0,c[t]=cnt;
                vmin[cnt]=min(vmin[cnt],mn[t]),vmax[cnt]=max(vmax[cnt],mx[t]);
            }while(t!=x);
        }
    }
    int main() {
        read(n);
        build(1,n,1);
        for(int i=1;i<=n;i++) {
            read(X[i]); read(R[i]);
            mn[pos[i]]=mx[pos[i]]=X[i];
        }
        for(int i=1;i<=n;i++) {
            connect(lower_bound(X+1,X+n+1,X[i]-R[i])-X,upper_bound(X+1,X+n+1,X[i]+R[i])-X-1,pos[i],1,n,1);
            //cout<<pos[i]<<endl;
        }
        for(int i=1;i<=4*n;i++) {
            if(!dfn[i])
            {
                tarjan(i);
                //cout<<i<<endl;
            }
        }
        //cout<<cnt<<endl;
        int cc=0;
        for(int x=1;x<=n*4;x++ )
            for(int i=lin[x];i;i=a[i].next)
                    if(c[x]!=c[a[i].y])
                        e[++cc].y=c[x],e[cc].next=linc[c[a[i].y]],linc[c[a[i].y]]=cc,rd[c[x]]++;
        
        for(int i=1;i<=cnt;i++) {
            if(!rd[a[i].y]) q.push(a[i].y);
        }
        while(!q.empty())
        {
            int x=q.front();q.pop();
            for(int i=linc[x];i;i=e[i].next)
            {
                vmin[e[i].y]=min(vmin[e[i].y],vmin[x]),vmax[e[i].y]=max(vmax[e[i].y],vmax[x]),rd[e[i].y]--;
                if(!rd[e[i].y]) q.push(e[i].y);
            }
        }
        LL ans=0;
        for(int i = 1 ; i <= n ; i ++ )
            ans=(ans+(long long)(upper_bound(X+1,X+n+1,vmax[c[pos[i]]])-lower_bound(X+1,X+n+1,vmin[c[pos[i]]]))*i) % 1000000007;
        printf("%lld
    " , ans%1000000007);
        return 0;
    } 
    View Code
  • 相关阅读:
    leetcode 141. Linked List Cycle
    leetcode 367. Valid Perfect Square
    leetcode150 Evaluate Reverse Polish Notation
    小a与星际探索
    D. Diverse Garland
    C. Nice Garland
    数的划分(动态规划)
    平衡二叉树(笔记)
    1346:【例4-7】亲戚(relation)
    1192:放苹果(dp + 搜索)
  • 原文地址:https://www.cnblogs.com/Tyouchie/p/11144165.html
Copyright © 2011-2022 走看看