CF605B的题解

算是对 Leap_Frog大佬的补充吧qwq

%%% Leap_Frog.

我们来看一下大佬的这段话:

考虑倒着思考 Kruskal 算法。
按边权从小到大排序。
每次插入一条边。
如果是树边,那就新开节点。
否则在当前节点内任意连边。
这样构造,每次非树边插入都比当前两端小。
所以必然正确。

对于“如果是树边,那就新开节点。”这句话,可以理解成我们构造一个以 1 号点为中心的菊花图(所有点都连向 1 号点)。对于第 i 条树边,建一条从 1 连向 i+1 的边。

就是这样子:

对于“否则在当前节点内任意连边。”这句话,我们插入非树边的时候为了不能重复(题目有要求两点之间不能有多条边),设我们目前已经处理了 i 条树边(即目前我们已经开了 i+1 个点),现在要插的边是从 u 连向 v,那么我们 u 可以从 j 开始 (3ji+1)v2 开始建边。每建完一条边,v 就加 1,如果 u=v,那么 u1v 回退到 2 重新开始为下一次的建边做准备。

1 的情况就是我们目前开的点不够用(即 u=i+1,v=i)。

放几张图理解一下吧qwq:

这样子代码是不是好理解一点啦qwq.

没有压行的代码(Leap_Frog大佬压行的代码感觉不太好读TaT):

#include<cstdio>
#include<algorithm>
using namespace std;
int n,m;
struct edge
{
	int u;
	int v;
	int w;
	int id;
	int yes;
}e[100010];
int tot=1;//目前已开的点的数量,我们要设为1,因为我们得先开好1号节点
bool cmp1(edge a,edge b)
{
	if(a.w==b.w) return a.yes>b.yes;
	return a.w<b.w;
}
bool cmp2(edge a,edge b)
{
	return a.id<b.id;
}
int u=2,v=3;//记得初始化
bool flag=1;
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&e[i].w,&e[i].yes);
		e[i].id=i;
	}
	sort(e+1,e+1+m,cmp1);
	for(int i=1;i<=m;i++)
	{
		if(e[i].yes) e[i].u=1,e[i].v=++tot;//如果是树边
		else//如果是非树边
		{
			if(v>tot)//无解
			{
				flag=0;
				break;
			}
			e[i].u=u++;
			e[i].v=v;
			if(u==v) v++,u=2;
		}
	}
	if(!flag) printf("-1");
	else
	{
		sort(e+1,e+1+m,cmp2);//最后要记得按一开始的输入顺序排序
		for(int i=1;i<=m;i++) printf("%d %d\n",e[i].u,e[i].v);
	}
	return 0;
}

完结撒花~

posted @   osfly  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示