洛谷 P3469 BLO-Blockade 【tarjan割点】

本文详细解析洛谷P3469题目,介绍了一种算法来解决程序员罢工导致城镇封锁的问题,通过计算每个城镇被封锁时无法进行的访问数量,帮助理解网络连通性和割点的概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


传送门:洛谷 P3469



题目描述
在Byteotia有n个城镇。 一些城镇之间由无向边连接。 在城镇外没有十字路口,尽管可能有桥,隧道或者高架公路(反正不考虑这些)。每两个城镇之间至多只有一条直接连接的道路。人们可以从任意一个城镇直接或间接到达另一个城镇。 每个城镇都有一个公民,他们被孤独所困扰。事实证明,每个公民都想拜访其他所有公民一次(在主人所在的城镇)。所以,一共会有n*(n-1)次拜访。

不幸的是,一个程序员总罢工正在进行中,那些程序员迫切要求购买某个软件。

作为抗议行动,程序员们计划封锁一些城镇,阻止人们进入,离开或者路过那里。

正如我们所说,他们正在讨论选择哪些城镇会导致最严重的后果。

编写一个程序:

读入Byteotia的道路系统,对于每个被决定的城镇,如果它被封锁,有多少访问不会发生,输出结果。

输入输出格式
第一行读入n,m,分别是城镇数目和道路数目(1≤n≤100 000, 1≤m≤500 000)

城镇编号1~n

接下来m行每行两个数字a,b,表示a和b之间有有一条无向边

输出n行,每行一个数字,为第i个城镇被锁时不能发生的访问的数量。

输入 #1
5 5
1 2
2 3
1 3
3 4
4 5

输出 #1
8
8
16
14
8



对于非割点,答案是2(n-1) (因为它不能影响别的点对连通性,能影响的只是别人到它以及它到别人)

对于割点,去掉后一个强连通分量会变成几块,那么这几块中不同块的两个点肯定就无法联通了,答案也就是每组块的点的数量互相乘出来,再加上2(n-1)

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<stack>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int N=110000;
int n,m,idx,k;
int dfn[N],low[N],size[N];
int vis[N],head[N];
ll ans[N];
vector<ll> g[N];//存割去i点后,剩下几块,每块的点的数目
struct edge
{
  int to,next;
}e[10*N];
void add(int u,int v)
{
  e[k].to=v;
  e[k].next=head[u];
  head[u]=k++;
}
void tarjan(int u)
{
  int v;
  dfn[u]=low[u]=++idx;
  size[u]++;
  int temp=0;
  for(int i=head[u];i!=-1;i=e[i].next)
  {
    v=e[i].to;
    if(!dfn[v])
    {
      tarjan(v);
      size[u]+=size[v];
      low[u]=min(low[u],low[v]);
      if(low[v]>=dfn[u])
      {
        ans[u]+=(ll)temp*size[v];
        temp+=size[v];
        //vis[u]=1;
        //g[u].push_back(size[v]);
      }
    }
    else
    {
      low[u]=min(low[u],dfn[v]);
    }
  }
  ans[u]+=(ll)temp*(n-temp-1);
  /*if(vis[u]&&n-temp-1!=0)
  {
    g[u].push_back(n-temp-1);
  }*/
}
int main()
{
  int a,b;
  memset(head,-1,sizeof(head));
  k=1,idx=0;
  scanf("%d%d",&n,&m);
  for(int i=0;i<m;i++)
  {
    scanf("%d%d",&a,&b);
    add(a,b);
    add(b,a);
  }
  for(int i=1;i<=n;i++)
  {
    if(!dfn[i])
    {
      tarjan(i);
    }
  }
  for(int i=1;i<=n;i++)
  {
    printf("%I64d\n",(ans[i]+n-1)*2);
  }

  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值