zoukankan      html  css  js  c++  java
  • 手铐-牛客(tarjan+dfs)

    手铐-牛客(tarjan+dfs)

    题意:

    给你一个连通无向图,保证每个点最多属于一个简单环,每个点度数最多为3,求这个图有多少“手铐图形个数”

    其中“手铐图形个数”,定义为三元组(x,y,S),其中x和y表示图上的两个点,S表示一条x到y的简单路径,而且必须满足:

    1.x和y分别在两个不同的简单环上

    2.x所在的简单环与路径S的所有交点仅有x,y所在的简单环与路径S的所有交点仅有y。

    (x,y,S)与(y,x,S)算同一个手铐;原图:

     

     

    缩点之后:

     

    缩点之后变成方点:

     

    由1.4构成的手铐有4个,可以走上边也可以走下边,

    假设在两个方点之间有x个方点,则构成的手铐数量为2^x;

    tarjan缩点成无向图之后,就变成了树,然后进行树形dp;

     

     

     

    数组维护手铐的值:

    考虑u的所有子树中点数超过1(方点)的块走到u的方案数dp[u],以此考虑u的儿子,对于第一个儿子v,若u也是点大于1的块,那么第一个儿子中点数大于1的点到u构成的手铐对答案的贡献就是dp[v],对于其他儿子,可以经过u点与之前考虑的儿子中点数大于1的点构成手铐,此时对答案的贡献就是dp[u]⋅dp[v]。

    对于dp[u]的维护,如果u点数大于1,那么dp[u]+=2⋅dp[v],否则dp[u]+=dp[v],时间复杂度O(n+m)

    将节点当成半手铐看;dp[u]表示以u为父节点的半手铐数量

    1,圆形节点 dp[u]+=dp[v];

    2,方形节点 dp[u]+=dp[v]*2;

    ans[u]表示以u为父节点的手铐数,v是子节点;

    ans[u]+=f[u]*f[v]+ans[v];

    代码:

    #include<bits/stdc++.h>
    /*#include<iostream>
    #include<string>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<iomanip>
    #include<queue>
    #include<cstring>*/
    using namespace std;
    const int maxn=1e6+10;
    const int mod=19260817;
    const int inf=0x3f3f3f3f;
    typedef long long ll;
    typedef pair<int,int> pii;
    const int N=5e5+10;
    
    inline int read()
    {
        int x=0,w=0;
        char ch=getchar();
        while (!isdigit(ch))
           w|=ch=='-',ch=getchar();
        while (isdigit(ch))
           x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
        return w?-x:x;
    }
    
    struct node{
        int v,next;
    }G[maxn<<2];
    int head[maxn<<1],cnt;
    inline void add(int x,int y)
    {
        G[cnt]=(node){y,head[x]};
        head[x]=cnt++;
    }
    
    int dfn[maxn],low[maxn],tot;
    stack<int> st;
    //int Stack[maxn],top;
    int vis[maxn];
    int belong[maxn];
    int num;
    ll f[maxn];
    int cot[maxn];
    
    void tarjan(int u,int pre)
    {
        dfn[u]=low[u]=++tot;
        st.push(u);
        vis[u]=1;
        for(int i=head[u]; i!=-1; i=G[i].next)
        {
            if(G[i].v==pre) continue;
            int v=G[i].v;
            if(!dfn[v])
            {
                tarjan(v,u);
                low[u]=min(low[u],low[v]);
            }
            else if(vis[v])
             low[u]=min(low[u],dfn[v]);
        }
        if(dfn[u]==low[u])
        {
            ++num;
            int t;
            do{
                t=st.top();
                st.pop();//缩点
                ++cot[num];//判断方点还是圆点
                vis[t]=0;
                belong[t]=num;//t属于num所代表的节点
            }while(u!=t);
        }
    }
    
    ll dp[maxn];
    vector <int> g[maxn];
    //ll tmp[maxn];
    
    void dfs(int u,int fa)//当前节点,父节点
    {
        dp[u]=0;//初始值
        f[u]=(cot[u]>1);//1.0标记圆方点
        for(int v:g[u])
        {
            //int v=G[i].v;
            if(v==fa) continue;//与u相连的节点是父节点跳过这个点,剪枝
            dfs(v,u);
            dp[u]=(1ll*dp[u]+f[v]*f[u]+dp[v])%mod;//计算手铐数
            f[u]=(1ll*f[u]+f[v]*(cot[u]>1?2:1))%mod;
            //f[u]=f[u]>1;   f[u]=(1ll*f[u]+f[v]*(f[u]==1?2:1))%mod;过不了
            //f[u]的值一直在变化,如果不分开存储,会出现问题。
        }
    }
    
    int n,m;
    int main()
    {
        n=read(),m=read();
        for(int i=0; i<=n; i++)
         head[i]=-1;
        for(int i=1; i<=m; i++)
        {
            int u,v;
            u=read(),v=read();
            add(u,v);
            add(v,u);
        }
        for(int i=1; i<=n; i++)
        {
            if(!dfn[i])
             tarjan(i,0);//缩点
        }
    
        for(int i=1; i<=n; i++)//构造新图
        {
            for(int j=head[i]; j!=-1; j=G[j].next)
            {
                int u=belong[i],v=belong[G[j].v];
                if(u!=v)
                 g[u].push_back(v);
            }
        }
        dfs(1,0);
        cout<<dp[1]<<endl;//答案
        system("pause");
        return 0;
    }

     

  • 相关阅读:
    为何没有.aspx.designer.cs文件?
    ItemDataBound的用法
    DataList控件 属性全攻略
    给LinkButton添加href、target属性
    给文本框添加灰色提示文字
    转载::深入研究DataList分页方法
    WCF 第六章 序列化与编码 保留引用和循环引用
    WCF 第六章 序列化和编码之NetDataContractSerializer
    WCF 第六章 序列化与编码 比较WCF序列化选项
    WCF 第六章 序列化和编码 使用代理序列化类型
  • 原文地址:https://www.cnblogs.com/sweetlittlebaby/p/13460600.html
Copyright © 2011-2022 走看看