4594: [Shoi2015]零件组装机

本文介绍了一种名为“零件组装机”的虚构设备及其背后的算法挑战。该设备能够生产及组合带有特定编号的无向图零件,文章详细阐述了如何通过算法判断给定的无向图是否能由该设备制造出来,提供了具体的实现代码,并分析了算法的时间复杂度。

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

4594: [Shoi2015]零件组装机

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 69   Solved: 26
[ Submit][ Status][ Discuss]

Description

 曾经发明了激光发生器的发明家SHTSC又公开了他的新发明:零件组装机--一种可以生产并组装零件的神秘装置。

一个零件是一张顶点由0 到n-1标号的无向图,零件组装机由以下两条功能。
(1)生产一个仅有一个顶点标号为0而没有边的零件。
(2)组合两个已有的零件G1,G2,且G2的顶点数m>=G1的顶点数n,得到新的零件G。G的顶点集合是G1、G2顶点集合的并
集,并且G2的顶点i(0<=i<m)被重新标号为n+i。G的边集是G1、G2边集的并集再对所有标号为a(a>=n)的顶点添加一
条连接(a,a mod n)的无向边。
现在SHTSC正在思考,对于一个给定的零件,能否由零件组装机生产组装得到。注意:零件是带标号的,这意味着
两个零件即使仅有标号不同也被视为不同的零件。为了帮助你理解问题,SHTSC特地给了你顶点数<=5的零件的图例。

Input

第一行一个整数t,表示有t组数据。

每组数据的第一行两个整数n,m。表示某个带标号的无向图有n个顶点0到n-1标号,m是边的数量。
接下来m行,每行两个整数u,v表示一条从u到v的无向边。
t<=10,n,m<=100000,0<=u, v<n

Output

对于每组数据,输出一行。如果这个无向图可以被零件制造机制造输出"YES"否则输出"NO"。

Sample Input

2
1 0
2 0

Sample Output

YES
NO
//样例1:n=1的情况和一个不能被产生的零件

HINT

Source

[ Submit][ Status][ Discuss]

这图的生成很有规律---(也得啊)
每一次组合两张图,多出来的边一定是恰好m条
这m条又刚刚好是G1与G2唯一连接的方式
而且是顺次连过去的,,
假设一张n个点m条边的图G是由G1G2组合而来
那么一定能在G中找到一个点k,使得k之后的点和前面k个点的连接恰好是siz - k条
而且这siz - k条边左边的编号一定是0,1,2...k
显然,这样的k是唯一的,因为往左移或是往右移都会破坏这样的平衡
那么就递归去搞,每次把图拆成两半。。
而每次递归至少删除n/2条边,于是复杂度最坏不过O(nlogn)


最后。。。如果给出重边一定是NO...GG
一开始写没特判重边,而且边的传递用vector被卡内存了。。。。惨
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<map>
#include<stack>
#include<set>
#include<cmath>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int maxn = 1E5 + 10;

struct E{
	int x,y;
	E(){}
	E(int x,int y): x(x),y(y){}
	bool operator < (const E &b) const {return x < b.x;}
}e[maxn];

int t,L[maxn];
bool bo[maxn];

bool Judge(int n,int l,int r)
{
	if (n == 1) return l == r;
	if (l == r) return 0;
	for (int i = 0; i < n; i++) 
		L[i] = maxn,bo[i] = 0;
	for (int i = l; i < r; i++) {
		int x = e[i].x;
		int y = e[i].y;
		if (x == L[y]) return 0;
		L[y] = min(L[y],x);
	}
	for (int i = 0; i < n; i++) 
		if (L[i] == 0) bo[i] = 1;
		else if (L[i] - L[i-1] == 1 && bo[i-1]) bo[i] = 1;
	int flag = 0,siz;
	for (int i = 0; i < n/2; i++) {
		siz = i + 1;
		bool ok = 1;
		for (int j = i + siz; j < n; j += siz) {
			if (bo[j] && L[j] == i) continue;
			ok = 0;	break;
		}
		if (ok) {flag = 1; break;}
	}
	if (!flag) return 0;
	int p1 = l,p2;
	for (int i = l; i < r; i++) {
		int x = e[i].x;
		int y = e[i].y;
		if (x == siz) break;
		if (y < siz) e[p1++] = e[i];
	}
	p2 = p1;
	for (int i = l; i < r; i++) {
		int x = e[i].x;
		int y = e[i].y;
		if (x < siz) continue;
		e[p2++] = E(e[i].x - siz,e[i].y - siz);
	}
	return Judge(siz,l,p1) && Judge(n - siz,p1,p2);
}

int getint()
{
	char ch = getchar();
	int ret = 0;
	while (ch < '0' || '9' < ch) ch = getchar();
	while ('0' <= ch && ch <= '9')
		ret = ret*10 + ch - '0',ch = getchar();
	return ret;
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	t = getint();
	while (t--) {
		int n,m;
		n = getint();
		m = getint();
		for (int i = 0; i < m; i++) {
			int x = getint();
			int y = getint();
			e[i] = E(min(x,y),max(x,y));
		} 
		sort(e,e + m);
		if (Judge(n,0,m)) puts("YES");
		else puts("NO");
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值