题解:BLO-Blockade
本文仅发布于此博客和作者的洛谷博客,不允许任何人以任何形式转载,无论是否标明出处及作者。
柿子比较毒瘤(可能还是做的题有点少罢(
题意
给你一张个点,条边的无向图,保证连通。
对于每一个节点,请给出在图中删除和相连的所有边后,不再连通的有序点对的数量。
注意,删除边的操作是暂时的(就是删除和相连的边时,之前删除的和相连的边会恢复),且删除边的时候点会仍然保留。
思路
首先,连接一个点的所有边被删掉以后,图肯定会裂成几个连通块。设任意一个点所在的连通块里面的点的数量为,那么有几个点可以和组成一个满足题目要求的有序对呢?个。(内的所有点都不能和组成有序对,以外的都可以)
内总共有个点,每个点都有相同的个点可以组成有序对,整个连通块的所有点和其他点可以组成的有序对数量就是,我们称这个数量为的贡献。
显然,题目所求就是所有连通块的贡献之和。我们看一下整个图会裂成哪几种连通块。
这是一个图,我们把和3相连的边去掉
发现,裂成的连通块总共有三种:
-
主体部分(1,2,10)
-
(没有连接到主体部分上的) 以3的儿子为根的子树(9)和(4,5,6,7,8)
-
3本身(3)
我们发现,一个图在删掉所有连接点的边后,会分裂成块。
块“子树”,块“”,块“主体”.
对于块“子树”,它们的贡献之和是。
对于“”,贡献是。
因为“主体”是原图的所有节点刨去“子树”和“”的部分,剩余下来的,所以“主体”的节点数为。
所以对于“主体”,贡献是.
于是,我们就可以得到删除和相连的所有边后,不再连通的有序点对的数量:
好的,那我们的柿子就推到这里就可以了。
那么我们就需要求出来删掉点后,每个没有和主体连接的,以的儿子为根的子树的大小。
考虑使用Tarjan算法,在求割点的代码基础上作一点改造。
分成两部分,一部分找到子树,一部分计算子树的大小。
先看第一部分。
在判定割点的时候,如果low[g[k][i]]>=dfn[k]
,就代表以g[k][i]
这个儿子为根的子树没有连接上主体。
此时,我们记录g[k][i]
,就找到了一个没连接上主体的子树。
而第二部分可以在dfs中顺带实现,计算以所有点为根的子树大小不难。
和割点不同的是,在dfs根节点的时候,完全不需要进行额外的处理。
处理根节点时,我们把主体看成个点,显然所有子树都与其不连通。这时,可以直接套用柿子不会出错。
#include<bits/stdc++.h>
#define int long long//十年OI一场空,不开long long见祖宗!!1
using namespace std;
int n,m;
vector<int> g[100005];
int dfn[100005];
int low[100005];
int fa[100005];
int subtree[100005];//一个点的子树大小
int sum[100005];//就是题目要求求的那个有序对数量了
int cnt=0;
void calc(int k,vector<int> child){
int remains=n;//主体中点的数量
remains--;//k自己是一个连通块,不在主体中
for(int i=0;i<child.size();i++){
remains-=subtree[child[i]];//没连上主体的子树,不在主体中。
}
sum[k]+=remains*(n-remains);//主体计数
for(int i=0;i<child.size();i++){
sum[k]+=subtree[child[i]]*(n-subtree[child[i]]);//没连上主体的子树计数
}
sum[k]+=1*(n-1);//k自己计数
}
void tarjan(int k){//注意这里没有了root
vector<int> child;//存储没连上主体的子树
child.clear();
cnt++;
dfn[k]=cnt;
low[k]=cnt;
subtree[k]=1;//子树大小。先算上自己。
for(int i=0;i<g[k].size();i++){
if(g[k][i]==fa[k]){//如果是父亲节点就不搜
continue;
}
if(dfn[g[k][i]]==0){
fa[g[k][i]]=k;
tarjan(g[k][i]);
low[k]=min(low[k],low[g[k][i]]);//Tarjan标准操作 更新low值
subtree[k]+=subtree[g[k][i]];//子树大小更新。
if(low[g[k][i]]>=dfn[k]){//这里是发现一个没连上主体的子树
child.push_back(g[k][i]);//记录一下,等着一起扔进柿子里算。
}
}else{
low[k]=min(low[k],dfn[g[k][i]]);//Tarjan标准操作*2
}
}
calc(k,child);//把子树什么的扔进柿子里算就完了
}
signed main(){
int tmpa,tmpb;
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>tmpa>>tmpb;
g[tmpa].push_back(tmpb);
g[tmpb].push_back(tmpa);
}
tarjan(1);//无向图本身连通,随便一个节点开始dfs就好
for(int i=1;i<=n;i++){
cout<<sum[i]<<endl;
}
}
圆满!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】