Live2D

Solution -「JOISC 2020」「UOJ #509」迷路的猫

Decription

  Link.

  这是一道通信题。

  给定一个 n 个点 m 条边的连通无向图与两个限制 A,B

  程序 Anthony 需要用 0A1A 中颜色为无向图的每条边染色。

  程序 Catherine 需要帮助一只猫行走:已知猫所在结点邻接每种颜色的边的数量,你需要告诉猫走哪种颜色的边(但不能让它走特定某条),并保证猫从起点 s0 所走的距离不超过两点最短距离 +B

  n,m2×104A,B 满足 A3,B0A2,B6 的一种,满足后者时,m=n1

Solution

  就**离谱好吗 qwq!

Anthony

  嘛……不难看出两种情况是割裂的,分类讨论咯。

Part 1

  A=3,B=0,相当于要求我们必须走最短路

  考虑从 0 出发,用 BFS 将图分层,设结点到 0 的距离为 disti 的点都在第 disti 层。注意猫是要从终点向回走,所以我们应当把连接上一层的边和连接同层连接下一层的边区分开来。一种方法是令 color(u,v)=min{distu,distv}mod3,则一个点的邻接边颜色不超过两种,判一判那条边是向上层的即可。

Part 1Code

namespace Task1 {

bool vis[MAXN + 5];

inline vecint main ( vecint U, vecint V ) {
	std::queue<int> que;
	que.push ( 0 ), vis[0] = true;
	while ( ! que.empty () ) {
		int u = que.front (); que.pop ();
		for ( pii v: graph[u] ) if ( ! vis[v.first] ) {
			dist[v.first] = dist[u] + 1;
			que.push ( v.first ), vis[v.first] = true;
		}
	}
	vecint ret ( m );
	for ( int i = 0; i < m; ++ i ) ret[i] = std::min ( dist[U[i]], dist[V[i]] ) % 3;
	return ret;
}

} // namespace RybyA::Task1.

Part 2

  A=2,B=6,一棵树。如果以 0 为根,无脑往上走就好啦。问题在于如何将一个结点的父亲与儿子们区分开来。

  若 du>2,即 u 有多于一个儿子,那么令向儿子们的颜色相同,向父亲的颜色与之区分即可。

  若 du=2,即链。怎么区分上下呢……我们考虑用一串循环颜色构造一个“通行方向箭头”,这样能让猫在走了一定的远路之后,可以认出箭头的指向继而调转方向。可见,“箭头”需要满足:所有由其循环同构串作为循环节的串不存在长度 B2+2 的回文子串。说人话呢,算上起点,猫最多用长度为 B2+1 的链“认路”,那么猫最多看到 B2+2 条边的颜色。如果颜色是回文,还是认不出路,就失败啦!

  一种可行的“箭头”是 0 0 1 1 0 1,恰好满足条件(0 1 1 0 为最长回文,长度为 4)。

Part 2Code

namespace Task2 {

const int dir[] { 0, 0, 1, 1, 0, 1 };
vecint ans;

inline void direct ( const int u, const int f, const int clen, const int curc ) {
// clen为链长(不在链上则为0),curc是若u不在链上时需要为向儿子的边染的颜色。
    if ( ! ~ f ) {
		for ( pii v: graph[u] ) {
			ans[v.second] = 0;
			direct ( v.first, u, ( int ) graph[u].size () == 1, 1 );
		}
		return ;
	}
	if ( ( int ) graph[u].size () == 2 ) {
		for ( pii v: graph[u] ) if ( v.first ^ f ) {
			ans[v.second] = dir[clen % 6];
			direct ( v.first, u, clen + 1, ans[v.second] ^ 1 );
		}
		return ;
	}
	for ( pii v: graph[u] ) if ( v.first ^ f ) {
		ans[v.second] = curc;
		direct ( v.first, u, 0, ans[v.second] ^ 1 );
	}
}

inline vecint main () {
	ans.resize ( m );
	direct ( 0, -1, 0, 0 );
	return ans;
}

} // namespace RybyA::Task2.

Catherine

  大 模 拟 !

Part 1

  显。(因为下一部分太复杂 qwq……

Part 1Code

namespace Task1 {

inline int main ( vecint Y ) {
// last 为上一次走的颜色,Part2同理。
	int cnt = 0;
	if ( ~ last ) ++ Y[last];
	for ( int y: Y ) cnt += !! y;
	if ( cnt == 1 ) last = Y[2] ? 2 : !! Y[1];
	else {
		for ( int i = 0; i < 3; ++ i ) {
			if ( ! Y[i] ) {
				last = ( i + 1 ) % 3;
				break;
			}
		}
	}
	return last;
}

} // namespace RybyC::Task1.

Part 2

  首先,记录是否已经定好方向。若已定向,无脑走即可。若未定向且当前点仍在链上,则全局用一个 std::vector 记下经过的箭头颜色。当 std::vectorsize()5,就一定可以定向了,打表判一判即可。

  反正……亿点细节,见代码吧(绝望 qwq。

Part 2Code

namespace Task2 {

const int down[6][5] {
	{ 0, 0, 1, 1, 0 },
	{ 0, 1, 1, 0, 1 },
	{ 1, 1, 0, 1, 0 },
	{ 1, 0, 1, 0, 0 },
	{ 0, 1, 0, 0, 1 },
	{ 1, 0, 0, 1, 1 }
};
vecint chain;
bool directed;

inline int main ( vecint Y ) { // 注意需要在外部用返回值更新last。
	if ( ! ~ last ) {
		if ( Y[0] + Y[1] == 2 ) {
			if ( ! Y[1] ) {
				chain.push_back ( 0 ), chain.push_back ( 0 );
				return 0;
			} else if ( ! Y[0] ) {
				chain.push_back ( 1 ), chain.push_back ( 1 );
				return 1;
			} else {
				chain.push_back ( 1 ), chain.push_back ( 0 );
				return 0;
			}
		}
		if ( ! Y[0] ) return directed = true, 1;
		if ( ! Y[1] ) return directed = true, 0;
		return directed = true, Y[1] == 1;
	}
	if ( ! Y[0] && ! Y[1] ) return directed = true, -1;
	if ( ! directed ) {
		if ( Y[0] + Y[1] > 1 ) {
			directed = true;
			if ( ! Y[0] || ! Y[1] ) return -1;
			return ! last;
		}
		chain.push_back ( Y[1] );
		if ( chain.size () == 5 ) {
			bool downing = false; directed = true;
			for ( int i = 0; ! downing && i < 6; ++ i ) {
				int j = 0;
				for ( ; j < 5; ++ j ) if ( down[i][j] ^ chain[j] ) break;
				if ( j == 5 ) downing = true;
			}
			if ( downing ) return -1;
		}
		return chain.back ();
	} else {
		if ( ! Y[0] ) return 1;
		if ( ! Y[1] ) return 0;
		++ Y[last];
		return Y[1] == 1;
	}
}

} // namespace RybyC::Task2.

} // namespace RybyC.

Code

// Anthony.cpp

#include <queue>
#include <vector>
#include <iostream>
#include "Anthony.h"

#ifndef vecint
#define vecint std::vector<int>
#endif
#ifndef pii
#define pii std::pair<int, int>
#endif

namespace RybyA {

const int MAXN = 2e4;
int n, m, dist[MAXN + 5];
std::vector<pii> graph[MAXN + 5];

namespace Task1 {

bool vis[MAXN + 5];

inline vecint main ( vecint U, vecint V ) {
	std::queue<int> que;
	que.push ( 0 ), vis[0] = true;
	while ( ! que.empty () ) {
		int u = que.front (); que.pop ();
		for ( pii v: graph[u] ) if ( ! vis[v.first] ) {
			dist[v.first] = dist[u] + 1;
			que.push ( v.first ), vis[v.first] = true;
		}
	}
	vecint ret ( m );
	for ( int i = 0; i < m; ++ i ) ret[i] = std::min ( dist[U[i]], dist[V[i]] ) % 3;
	return ret;
}

} // namespace RybyA::Task1.

namespace Task2 {

const int dir[] { 0, 0, 1, 1, 0, 1 };
vecint ans;

inline void direct ( const int u, const int f, const int clen, const int curc ) {
	if ( ! ~ f ) {
		for ( pii v: graph[u] ) {
			ans[v.second] = 0;
			direct ( v.first, u, ( int ) graph[u].size () == 1, 1 );
		}
		return ;
	}
	if ( ( int ) graph[u].size () == 2 ) {
		for ( pii v: graph[u] ) if ( v.first ^ f ) {
			ans[v.second] = dir[clen % 6];
			direct ( v.first, u, clen + 1, ans[v.second] ^ 1 );
		}
		return ;
	}
	for ( pii v: graph[u] ) if ( v.first ^ f ) {
		ans[v.second] = curc;
		direct ( v.first, u, 0, ans[v.second] ^ 1 );
	}
}

inline vecint main () {
	ans.resize ( m );
	direct ( 0, -1, 0, 0 );
	return ans;
}

} // namespace RybyA::Task2.

} // namespace RybyA.

vecint Mark ( const int N, const int M, const int A, const int B, vecint U, vecint V ) {
	RybyA::n = N, RybyA::m = M;
	for ( int i = 0; i < M; ++ i ) {
		RybyA::graph[U[i]].push_back ( { V[i], i } );
		RybyA::graph[V[i]].push_back ( { U[i], i } );
	}
	if ( A > 2 ) return RybyA::Task1::main ( U, V );
	return RybyA::Task2::main ();
}

// main () {}
// Catherine.cpp

#include <vector>
#include <assert.h>
#include "Catherine.h"

#ifndef vecint
#define vecint std::vector<int>
#endif
#ifndef pii
#define pii std::pair<int, int>
#endif

namespace RybyC {

bool type;
int last;

namespace Task1 {

inline int main ( vecint Y ) {
	int cnt = 0;
	if ( ~ last ) ++ Y[last];
	for ( int y: Y ) cnt += !! y;
	if ( cnt == 1 ) last = Y[2] ? 2 : !! Y[1];
	else {
		for ( int i = 0; i < 3; ++ i ) {
			if ( ! Y[i] ) {
				last = ( i + 1 ) % 3;
				break;
			}
		}
	}
	return last;
}

} // namespace RybyC::Task1.

namespace Task2 {

const int down[6][5] {
	{ 0, 0, 1, 1, 0 },
	{ 0, 1, 1, 0, 1 },
	{ 1, 1, 0, 1, 0 },
	{ 1, 0, 1, 0, 0 },
	{ 0, 1, 0, 0, 1 },
	{ 1, 0, 0, 1, 1 }
};
vecint chain;
bool directed;

inline int main ( vecint Y ) {
	if ( ! ~ last ) {
		if ( Y[0] + Y[1] == 2 ) {
			if ( ! Y[1] ) {
				chain.push_back ( 0 ), chain.push_back ( 0 );
				return 0;
			} else if ( ! Y[0] ) {
				chain.push_back ( 1 ), chain.push_back ( 1 );
				return 1;
			} else {
				chain.push_back ( 1 ), chain.push_back ( 0 );
				return 0;
			}
		}
		if ( ! Y[0] ) return directed = true, 1;
		if ( ! Y[1] ) return directed = true, 0;
		return directed = true, Y[1] == 1;
	}
	if ( ! Y[0] && ! Y[1] ) return directed = true, -1;
	if ( ! directed ) {
		if ( Y[0] + Y[1] > 1 ) {
			directed = true;
			if ( ! Y[0] || ! Y[1] ) return -1;
			return ! last;
		}
		chain.push_back ( Y[1] );
		if ( chain.size () == 5 ) {
			bool downing = false; directed = true;
			for ( int i = 0; ! downing && i < 6; ++ i ) {
				int j = 0;
				for ( ; j < 5; ++ j ) if ( down[i][j] ^ chain[j] ) break;
				if ( j == 5 ) downing = true;
			}
			if ( downing ) return -1;
		}
		return chain.back ();
	} else {
		if ( ! Y[0] ) return 1;
		if ( ! Y[1] ) return 0;
		++ Y[last];
		return Y[1] == 1;
	}
}

} // namespace RybyC::Task2.

} // namespace RybyC.

void Init ( int A, int B ) { RybyC::last = -1, RybyC::type = A > 2; }

int Move ( vecint Y ) {
	if ( RybyC::type ) return RybyC::Task1::main ( Y );
	else {
		int t = RybyC::Task2::main ( Y );
		if ( ~ t ) RybyC::last = t;
		return t;
	}
}

// main () {}
posted @   Rainybunny  阅读(168)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示