ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【tarjan】矿场搭建

2022-07-03 17:35:59  阅读:164  来源: 互联网

标签:tarjan 矿场 连通 标记 int MAX 割点 搭建 分量


一、题目传送门:P3225 [HNOI2012]矿场搭建 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

二、思路分析:题目抽象后时这样的——给定一图G(V,E),标记其中某些点,使去掉任意一节点后其余所有节点都能到达标记点,求标记节点数和方案数

  1、首先割点是不会被标记的,因为割点如果坍塌,其连接的连通分量将失去逃生机会;如下图,标记割点不可能是最优解。   ·              

 

 

 

   2、考虑每个连通分量与割点的连接情况:

  (1)若该连通分量没有连接割点,即孤立连通分量(如图中的9),那么其内部标记的点数a与连通分量包含的点个数b有关(a=1,b=1;a>=2,b=2);

  (2)若该连通分量只与一个割点相连(如图中的7),那么该分量里必须有一个标记点,因为若没有标记点,割点坍塌后该连通分量里的人无法逃生;

  (3)若该连通分量与两个及以上个割点相连(如图中的1,2,3,8),则该分量里没有标记点,因为无论哪一个割点坍塌,该分量里的人都可以通过其它的割点到另外的连通分量里逃生。

  3、证明了以上性质这个题就解决了,步骤如下

  (1)利用tarjan跑出所有割点;

  (2)通过DFS统计每一个连通分量中的普通节点个数、连接的割点个数;

  (3)根据上文性质计算所需标记点的个数;

  (4)根据乘法原理,计算总方案数(具体式子见代码)

三、Solution:

#include<bits/stdc++.h>
#define MAX 60000
using namespace std;
typedef long long ll;//答案可能很大,记得开long long
ll ans,sum=1;//救援出口个数,方案数
int n,m,root,num=1;
int dfn[MAX],low[MAX],Time=1;
int nums,cuts,cnt,cut[MAX];
int group,vis[MAX];
vector<int> G[MAX];
void AddG(int from,int to)
{
    G[from].push_back(to);
}
void read()
{
    for(int i=1;i<=m;i++)
    {
        int x,y;
        cin>>x>>y;
        n=max(n,max(x,y));
        AddG(x,y);
        AddG(y,x);
    }
}
void init()//有多组数据,记得初始化
{
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(cut,0,sizeof(cut));
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++)
    {
        G[i].clear();
    }
    n=0,m=0,root=0,ans=0,cnt=0,sum=1,Time=1;
}
void tarjan(int x)//tarjan模板,跑出所有割点
{
    dfn[x]=low[x]=Time++;
    if(x==root and !G[x].size())
    {
        return;
    }
    int flag=0;
    for(int i=0;i<int(G[x].size());i++)
    {
        int y=G[x][i];
        if(!dfn[y])
        {
            tarjan(y);
            low[x]=min(low[x],low[y]);
            if(low[y]>=dfn[x])
            {
                flag++;
                if(x!=root or flag>1) cut[x]=1;    
                cnt++;
            }
        }
        low[x]=min(low[x],dfn[y]);
    }
}
void DFS(int s)//通过DFS统计割点数和普通点数
{
    vis[s]=group;//打上标记,防止重复
    nums++;//普通点个数+1
    for(int i=0;i<int(G[s].size());i++)
    {
        int t=G[s][i];
        if(cut[t] and vis[t]!=group)//点t是割点且在该轮搜索中未曾访问
        {
            cuts++;//割点数量+1
            vis[t]=group;//打上标记

} if(!vis[t])//如果点t是普通点 { DFS(t);//继续搜索 } } } int main() { while(1) { init(); cin>>m; if(!m)return 0; read(); for(int i=1;i<=n;i++) { if(!dfn[i]) { root=i; tarjan(i); } } for(int i=1;i<=n;i++) { if(!vis[i] and !cut[i])//如果点i未曾访问过且不是割点 { group++; nums=cuts=0; DFS(i); if(cuts==0)//访问结束,若没有连接割点 { if(nums!=1) sum*=(nums-1)*nums/2,ans+=2;
            //如果普通点的个数大于等于2,根据乘法原理计算方案数sum,救援出口数+1 else ans++;
            //否则救援出口数+1,这种情况对方案数没有贡献 } if(cuts==1)//如果只连接了一个割点 { ans++; sum*=nums; } if(cuts==2)//连接了多个割点,没有贡献,不做处理 { } } } cout<<"Case "<<num++<<": "<<ans<<" "<<sum<<endl; } }

 

标签:tarjan,矿场,连通,标记,int,MAX,割点,搭建,分量
来源: https://www.cnblogs.com/dyctj2022/p/16440242.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有