首页 > 其他分享 >[题解]P3225 [HNOI2012] 矿场搭建

[题解]P3225 [HNOI2012] 矿场搭建

时间:2024-11-13 15:08:57浏览次数:1  
标签:连通 int 题解 割点 叶子 HNOI2012 dfn low P3225

P3225 [HNOI2012] 矿场搭建

挖煤点坍塌相当于把该点和与其相连的边在图上删掉。

借用wjyyy的题解,我们定义“叶子连通块”为“只包含\(1\)个割点的点双连通分量”,“非叶子连通块”为“包含\(\ge 2\)个割点的点双连通分量”。

如下图,橙色点是割点,红色框圈出的是点双,加粗的是叶子连通块。

叶子连通块只有\(1\)个割点,所以必须保证该连通块内部存在逃生出口,否则割点塌陷,里面的人就逃不出去了。显然逃生出口只要设置在此连通块的非割点处即可。

由于非叶子连通块有\(\ge 2\)个割点,所以就算其中一个割点塌陷,该连通块的人仍然可以通过其他未塌陷的割点跑到叶子连通块或者其他连通块去。既然任何一个直接可达的叶子连通块都已经存在逃生出口了,那就不必额外耗费资源去建逃生出口了。

所以这道题第\(1\)问的答案是叶子连通块的个数,第\(2\)问的答案是(每个叶子连通块的大小\(-1\))的乘积。

注意特判不存在叶子连通块(也就是整张图不存在割点)的情况,此时需要建\(2\)个逃生出口,以防其中一个塌陷。答案是\(C_n^2=\frac{n(n-1)}{2}\)。

时间复杂度:每组数据\(O(n)\)。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define N 1010
using namespace std;
int n,m,dfn[N],low[N],tim,cnt1,cnt2;
stack<int> p;
bitset<N> is,vis;
vector<int> G[N];
void tarjan(int u){
	dfn[u]=low[u]=++tim;
	int ch=0;
	for(int i:G[u]){
		if(!dfn[i]){
			tarjan(i),ch++;
			low[u]=min(low[u],low[i]);
			if(u!=1&&low[i]>=dfn[u]) is[u]=1;
		}else low[u]=min(low[u],dfn[i]);
	}
	if(u==1&&ch>=2) is[u]=1;
}
void dfs(int u){
	if(vis[u]) return;
	vis[u]=1,cnt2++;
	if(is[u]){
		p.push(u),cnt1++;
		return;
	}
	for(int i:G[u]) dfs(i);
}
void solve(int num){
	tarjan(1);
	int ans1,ans2;
	if(is.none()){
		ans1=2,ans2=n*(n-1)/2;
	}else{
		ans1=0,ans2=1;
		for(int i=1;i<=n;i++){
			if(is[i]) continue;
			cnt1=cnt2=0,dfs(i);
			while(!p.empty()) vis[p.top()]=0,p.pop();
			if(cnt1==1) ans1++,ans2*=(cnt2-1);
		}
	}
	cout<<"Case "<<num<<": "<<ans1<<" "<<ans2<<"\n";
}
signed main(){
	for(int koishi=1;;koishi++){
		memset(dfn,0,sizeof dfn);
		memset(low,0,sizeof low); 
		vis=is=tim=n=0;
		cin>>m;
		if(!m) break;
		for(int i=1,u,v;i<=m;i++){
			cin>>u>>v;
			G[u].emplace_back(v);
			G[v].emplace_back(u);
			n=max(n,max(u,v));
		}
		solve(koishi);
		for(int i=1;i<=n;i++) G[i].clear();
	}
	return 0;
}

标签:连通,int,题解,割点,叶子,HNOI2012,dfn,low,P3225
From: https://www.cnblogs.com/Sinktank/p/18543596

相关文章

  • P2612 [ZJOI2012] 波浪 题解
    前置知识:连续段dp题目链接:P2612[ZJOI2012]波浪随机一个\(1\)到\(n\)的排列\(P_{1...n}\),问以下式子的值\(\lem\)的概率是多少?\[|P_1-P_2|+|P_2-P_3|+|P_3-P_4|+...+|P_{n-1}+P_n|\]输出一个答案表示概率。保留\(k\)位小数。对于\(40%\)......
  • P11071 「QMSOI R1」 Distorted Fate题解
    题意:给定一个序列,给定两种操作:将一个区间异或上一个给定的值。给定\(l,r\)求\[{\large(\sum_{i=l}^r\bigcup_{j=l}^iA_j)\bmod2^{30}}\]\(0\lea_i,x<2^{30}\),\(1\lel\ler\len\)思路由于操作数以及区间过大,一位一位地去模拟肯定是不行的。因此考虑去离线......
  • [题解]CF1407D Discrete Centrifugal Jumps
    思路注意到第二个条件和第三个条件本质相似,可以用相同的维护方式处理,因此这个只讨论第二个条件的维护方式。定义\(dp_i\)表示走到\(i\)的最少步数。第一个条件的转移显然为\(dp_i\leftarrowdp_{i-1}\)。对于第二个条件,\(i\)能向\(j\)转移,当且仅当\(h_{i+1\sim......
  • Codeforces Round 985 div1+2 个人题解(A~E)
    CodeforcesRound985div1+2个人题解(A~E)Dashboard-Refact.aiMatch1(CodeforcesRound985)-Codeforces火车头#include<bits/stdc++.h>usingnamespacestd;#defineftfirst#definesdsecond#defineyescout<<"yes\n"#definenoc......
  • [CF1935E] Distance Learning Courses in MAC 题解
    [CF1935E]DistanceLearningCoursesinMAC难度正常的一道题。首先我们发现“挑选若干个区间”就是一句废话,因为按位或只会贡献答案而不会减小答案。所以我们需要在\([L,R]\)的每个区间都挑一个数,使得最终的按位或最大。想办法让尽可能多的二进制位都变成\(1\),且越是高......
  • [USACO06NOV] Corn Fields G 题解
    题目链接[USACO06NOV]CornFieldsG题解这是一道典型的状压dp,对于\(M\)行\(N\)列的图,由于每个点只有\(1\)和\(0\)两种状态,我们将其压缩为一个长度为\(M\)的数组,数组(\(g\))的每一项\(g_{i}\)表示状态的二进制表示法表示的数(如\(101\)表示为\(5\)),我们预先处......
  • [CEOI2023] A Light Inconvenience 题解
    Description今年CEOI的开幕式上有一场精彩的火炬表演。表演者们站成一排,从\(1\)开始从左往右编号。每个表演者举着一根火炬,初始只有一个举着点燃的火炬的表演者。表演分为\(Q\)幕。在第\(a\)幕开始之前,要么\(p_a>0\)个表演者从右侧加入表演,他们的火炬是熄灭的;要么最......
  • gym103102H AND = OR 题解
    非常巧妙的一个题。我们首先考虑单组询问该怎么做。首先需要注意到一个结论,即设答案为\(x\),那么对于\(\forally<x\),\(y\)都应该放在与组;同样的,对于\(\forally>x\),\(y\)都应该放在与组。进一步的,我们观察在\(\text{popcount}\)上也有同样的性质,即对于\(\forally,......
  • 好题记录 [集训队互测 2023] 优惠购物 题解
    首先发现这个过程的限制比较多,那么考虑重新描述这个过程。令\(x_i\)表示在第\(i\)个物品上使用了\(x_i\)张券,那么一组\(x_{1\simn}\)就描述了一个方案。方便起见,令\(s_i\)为前i个物品买完后剩了几张券,那么有:\(s_0=m\)\(s_i=s_{i-1}+\lfloor\frac{a_i-......
  • 【题解】洛谷P7287: 「EZEC-5」魔法
    P7287「EZEC-5」魔法感觉好题有思维,但是我没认真读题,看到样例就我以为了,他让任意一个区间满足大于\(S\)即可不是全部。我们手搓一下样例就可以发现,对于加法我们不加白不加的话肯定全部的数都加上,乘法肯定要等到加完后才开始,这些都是贪心思路。然后就是开始搭配操作了,我遇到......