\(\mathcal{Decription}\)
Link.
这是一道通信题。
给定一个 \(n\) 个点 \(m\) 条边的连通无向图与两个限制 \(A,B\)。
程序 Anthony
需要用 \(0\sim A-1\) 共 \(A\) 中颜色为无向图的每条边染色。
程序 Catherine
需要帮助一只猫行走:已知猫所在结点邻接每种颜色的边的数量,你需要告诉猫走哪种颜色的边(但不能让它走特定某条),并保证猫从起点 \(s\) 到 \(0\) 所走的距离不超过两点最短距离 \(+B\)。
\(n,m\le2\times10^4\),\(A,B\) 满足 \(A\ge 3,B\ge0\) 或 \(A\ge 2,B\ge6\) 的一种,满足后者时,\(m=n-1\)。
\(\mathcal{Solution}\)
就**离谱好吗 qwq!
\(\mathcal{Anthony}\)
嘛……不难看出两种情况是割裂的,分类讨论咯。
\(\mathcal{Part~1}\)
\(A=3,B=0\),相当于要求我们必须走最短路。
考虑从 \(0\) 出发,用 BFS 将图分层,设结点到 \(0\) 的距离为 \(dist_i\) 的点都在第 \(dist_i\) 层。注意猫是要从终点向回走,所以我们应当把连接上一层的边和连接同层或连接下一层的边区分开来。一种方法是令 \(color(u,v)=\min\{dist_u,dist_v\}\bmod3\),则一个点的邻接边颜色不超过两种,判一判那条边是向上层的即可。
\(\mathcal{Part~1-Code}\)
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.
\(\mathcal{Part~2}\)
\(A=2,B=6\),一棵树。如果以 \(0\) 为根,无脑往上走就好啦。问题在于如何将一个结点的父亲与儿子们区分开来。
若 \(d_u>2\),即 \(u\) 有多于一个儿子,那么令向儿子们的颜色相同,向父亲的颜色与之区分即可。
若 \(d_u=2\),即链。怎么区分上下呢……我们考虑用一串循环颜色构造一个“通行方向箭头”,这样能让猫在走了一定的远路之后,可以认出箭头的指向继而调转方向。可见,“箭头”需要满足:所有由其循环同构串作为循环节的串不存在长度 \(\ge\lfloor\frac{B}2\rfloor+2\) 的回文子串。说人话呢,算上起点,猫最多用长度为 \(\lfloor\frac{B}2\rfloor+1\) 的链“认路”,那么猫最多看到 \(\lfloor\frac{B}2\rfloor+2\) 条边的颜色。如果颜色是回文,还是认不出路,就失败啦!
一种可行的“箭头”是 0 0 1 1 0 1
,恰好满足条件(0 1 1 0
为最长回文,长度为 \(4\))。
\(\mathcal{Part~2-Code}\)
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.
\(\mathcal{Catherine}\)
大 模 拟 !
\(\mathcal{Part~1}\)
显。(因为下一部分太复杂 qwq……
\(\mathcal{Part~1-Code}\)
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.
\(\mathcal{Part~2}\)
首先,记录是否已经定好方向。若已定向,无脑走即可。若未定向且当前点仍在链上,则全局用一个 std::vector
记下经过的箭头颜色。当 std::vector
的 size()
为 \(5\),就一定可以定向了,打表判一判即可。
反正……亿点细节,见代码吧(绝望 qwq。
\(\mathcal{Part~2-Code}\)
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.
\(\mathcal{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 () {}