鸡腿の游戏

这是一篇关于经典图论问题的博客,探讨了当割点答案大于n-1时的解决方案。作者提到此题在某竞赛中被讨论,并指出对于非割点情况,答案通常是n-1。解题策略包括找到割点进行O(n)操作,以及通过构造新图将原图转换为树结构以简化问题。虽然直接暴力解法会超时,但作者分享了其高效的代码实现。

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

题目描述:

给出一个无向图,求图中每个点是多少对点对的必经点。

输入格式:

第一行输入两个正整数n,m,含义同题目描述。
接下来m行,每行两个整数a,b表示a,b两个通讯基站能够直接通讯。

输出格式:

输出n行,每行一个整数,第i行表示通讯基站i是多少对不同的通讯基站的必经点。

样例输入:

样例输出:

数据范围:

对于50%的数据n ≤ 5000,m≤10000;
对于100%的数据n ≤ 50000,m≤100000。

时间限制:

1S

空间限制:

256M



应该是很经典的题目了 ,,好像lyd在wc里也讲过的。。囧


一看肯定只有割点的答案大于n-1,非割点答案一定是n-1。

然后就找了个割点,然后以每个割点 O(n)做一遍乘一下。。然后就50分了。。

直接暴力还要T。。搞笑啊。。。


标算:

令原图为a,新建一个图为b,

求出a图每一个点双,在a中每一个点双对应b图新建一个点,每个点双上的点向新建点连边。

然后图就变成树了! 然后怎么搞都行了。。

时间不多了,要走了直接放代码好了。。


暴力:

#include <cstdio>
#include <algorithm>
#define rep(i,l,r) for (int i=l;i<=r;++i)
int getx(){
	char c;int x;
	for (c=getchar();c<'0'||c>'9';c=getchar());
	for (x=0;c>='0'&&c<='9';c=getchar())
		x=(x<<3)+(x<<1)+c-'0';
	return x;
}
bool upmin(int &a,const int &b){return a>b?a=b,1:0;}
const int MAX_N=50005,MAX_M=100005;
int first[MAX_N],next[MAX_M],to[MAX_M],tal=0;
void tjb(int x,int y){
	next[++tal]=first[x];
	first[x]=tal;to[tal]=y;
}
int dfn[MAX_N],low[MAX_N],Time;
bool cut[MAX_N],vis[MAX_N];
void tar(int v,int fa){
	dfn[v]=low[v]=++Time;
	vis[v]=true;
	int cnt=0;
	for (int k=first[v];k;k=next[k]){
		int u=to[k];
		if (u==fa) continue;
		if (!vis[u]) tar(u,v),cnt++;
		upmin(low[v],low[u]);
		if (!fa&&cnt>1) cut[v]=true;
		if (fa&&low[u]>=dfn[v]) cut[v]=true;
		}
}
int n,m;
int fa[MAX_N];
int getfa(int v){return fa[v]==v?fa[v]:(fa[v]=getfa(fa[v]));}
void dfs(int v){
	vis[v]=true;
	for (int k=first[v];k;k=next[k]){
		int u=to[k];
		if (vis[u]) continue;
		fa[getfa(u)]=getfa(v);
		dfs(u);
		}
}
int size[MAX_N];
bool px[MAX_N];
long long ans=0;
int main(){
	freopen("3.in","r",stdin);
	freopen("3.out","w",stdout);
	n=getx(),m=getx();
	rep(i,1,m){
		int x=getx(),y=getx();
		tjb(x,y),tjb(y,x);
		}
	tar(1,0);
	rep(i,1,n){
		if (cut[i]){
			ans=0;
			rep(j,1,n) size[j]=0,fa[j]=j,px[j]=vis[j]=0;
			vis[i]=true;
			for (int k=first[i];k;k=next[k])
				if (!vis[to[k]]) dfs(to[k]);
			rep(j,1,n) size[getfa(j)]++;
			int sum=0;
			for (int k=first[i];k;k=next[k]){
				int tp=getfa(to[k]);
				if (!px[tp]){px[tp]=true;sum+=size[tp];}
				}
			for (int k=first[i];k;k=next[k]){
				int tp=getfa(to[k]);
				if (px[tp]){
					px[tp]=false;
					ans+=(long long)(sum-size[tp])*size[tp];
					}
				}
			printf("%lld\n",ans/2+n-1);
			}
		else printf("%d\n",n-1);
		}
}

std:

#include <cstdio>
#include <algorithm>
#define rep(i,l,r) for (int i=l;i<=r;++i)
typedef long long LL;
bool upmin(int &a,const int &b){return a>b?a=b,1:0;}
int getx(){
	char c;int x;
	for (c=getchar();c<'0'||c>'9';c=getchar());
	for (x=0;c>='0'&&c<='9';c=getchar())
		x=(x<<3)+(x<<1)+c-'0';
	return x;
}
const int MAX_N=50005*2,MAX_M=100000*4;
struct n_i$s_h$i_d_a$s$b${
	int first[MAX_N],next[MAX_M],to[MAX_M],tal;
	int n;
	void tjb(int x,int y){
		next[++tal]=first[x];
		first[x]=tal;
		to[tal]=y;
		}
}a,b;
int dfn[MAX_N],low[MAX_N];
int sta[MAX_N],top,Time=0;
bool vis[MAX_N];
void tar(int v,int fa){
	dfn[v]=low[v]=++Time;
	vis[v]=true;
	for (int k=a.first[v];k;k=a.next[k]){
		int u=a.to[k];
		if (u==fa) continue;
		if (!vis[u]){
			sta[++top]=k;
			tar(u,v);
			upmin(low[v],low[u]);
			if (low[u]>=dfn[v]){
				b.n++;
				while (sta[top+1]!=k){
					u=sta[top--];
					b.tjb(b.n,a.to[u]);
					b.tjb(a.to[u],b.n);
					}
				b.tjb(b.n,v);b.tjb(v,b.n);
				}
			}else upmin(low[v],low[u]);
		}
}
int n,m;
int w[MAX_N];
LL ans[MAX_N];
int dfs(int v,int fa){
	int res=0;
	for (int k=b.first[v];k;k=b.next[k]){
		int u=b.to[k];
		if (u==fa) continue;
		res+=dfs(u,v);
		}
	ans[v]+=(n-1-res)*res;
	for (int k=b.first[v];k;k=b.next[k]){
		int u=b.to[k];
		if (u==fa) continue;
		ans[v]+=(LL)w[u]*(n-1-w[u]);
		}
	ans[v]=ans[v]/2+n-1;
	return w[v]=res+(v<=n);
}
int main(){
	freopen("3.in","r",stdin);
	freopen("3x.out","w",stdout);
	n=getx(),m=getx();
	rep(i,1,m){
		int x=getx(),y=getx();
		a.tjb(x,y),a.tjb(y,x);
		}
	b.n=n;
	tar(1,0);
	dfs(1,0);
	rep(i,1,n) printf("%lld\n",ans[i]);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值