gym102835I Critical Structures

https://codeforces.com/gym/102835/problem/I

比赛的时候想了半天为什么第二个样例不是2个极大边集,最大边集数是3

后来隔壁队告诉我们他们猜想是割边可以也当极大边集,然而他们wa了,于是我也写了一发也wa了

补题的时候发现这个猜想是对的,卧槽他第四种是边数,第三种也是边数,我都当点数算了,一般边双联通分量缩点都是统计点。。。

那么这题就是求割点割边双联通分量水题了


 

#include<bits/stdc++.h>
using namespace std;
 
const int maxl=1e6+10;
 
int n,m,cnt,ind,ans1,ans2,p,q,root,dcc;
int dfn[maxl],low[maxl],ehead[maxl];
int c[maxl],sz[maxl],szed[maxl];
struct ed{int to,nxt,id;}e[maxl<<1];
struct edg
{
	int u,v;
}edge[maxl];
bool cutd[maxl],cuted[maxl<<1],vis[maxl<<1];
 
inline void add(int u,int v,int id)
{
	e[++cnt].to=v;e[cnt].id=id;
	e[cnt].nxt=ehead[u];ehead[u]=cnt;
}
 
inline void prework()
{
	ind=0;ans1=0,ans2=0,p=0,q=0;cnt=1;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		dfn[i]=low[i]=ehead[i]=0;
		cutd[i]=false;
	}
	for(int i=1;i<=m;i++)
	{	
		cuted[i]=false;
		int u,v;scanf("%d%d",&u,&v);
		add(u,v,i);add(v,u,i);
		edge[i]=edg{u,v};
	}
}
 
inline void tarjand(int u,int fa)
{
	dfn[u]=++ind;low[u]=ind;
	int son=0,v;
	for(int i=ehead[u];i;i=e[i].nxt)
	{
		v=e[i].to;
		if(!dfn[v])
		{
			tarjand(v,u);
			low[u]=min(low[v],low[u]);
			if(low[v]>=dfn[u])
			{
				son++;
				if(u!=root || son>1)
					cutd[u]=true;
			}
		}
		else
			low[u]=min(low[u],dfn[v]);
	}
}
 
inline void tarjaned(int u,int last)
{
	dfn[u]=low[u]=++ind;
	int v;
	for(int i=ehead[u];i;i=e[i].nxt)
	{
		v=e[i].to;
		if(!dfn[v])
		{
			tarjaned(v,i);
			low[u]=min(low[u],low[v]);
			if(low[v]>dfn[u])
				cuted[e[i].id]=cuted[e[i^1].id]=true;
		}
		else if(i!=(last^1))
			low[u]=min(low[u],dfn[v]);
	}
}
 
inline void dfs(int u)
{
	int v;
	c[u]=dcc;sz[dcc]++;
	for(int i=ehead[u];i;i=e[i].nxt)
	{
		v=e[i].to;
		if(c[v] || cuted[e[i].id]) continue;
		dfs(v);
	}
}
 
inline void mainwork()
{
	for(int i=1;i<=n;i++)
	if(!dfn[i])
	{
		root=i;
		tarjand(i,i);
	}
	for(int i=1;i<=n;i++)
		dfn[i]=low[i]=c[i]=0;
	ind=0;
	tarjaned(1,0);
	dcc=0;
	for(int i=1;i<=n;i++)
	if(!c[i])
	{
		++dcc;sz[dcc]=0;szed[dcc]=0;
		dfs(i);
	}
	ans1=0;ans2=0;p=0;q=1;
	for(int i=1;i<=n;i++)
	if(cutd[i])
		++ans1;
	for(int i=1;i<=m;i++)
	{
		if(cuted[i])
			++ans2,++p;
		if(c[edge[i].u]==c[edge[i].v])
			++szed[c[edge[i].u]];
	}
	for(int i=1;i<=dcc;i++)
	{	
		q=max(szed[i],q);
		if(sz[i]>1)
			++p;
	}
	int d=__gcd(p,q);
	p/=d;q/=d;
}
 
inline void print()
{
	printf("%d %d %d %d\n",ans1,ans2,p,q);
}
 
int main()
{
	int t;
	scanf("%d",&t);
	for(int i=1;i<=t;i++)
	{
		prework();
		mainwork();
		print();
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值