KOI TST 2023 Day1 T4 야유회
KO Itst
神奇题。根本想不到。
题意
题意是你需要并行计算一个环的 k-coloring,初始时环上每个点的颜色被设置成它的标号(注意环上的标号被打乱了,并不连续),你需要实现三个函数:
- \(\texttt{morning(m,r)}\):给定环上当前节点和当前节点的下一个节点的初始颜色 \(m,r\),返回当前节点的新的颜色,颜色标号需在 \([0,10^9]\) 范围内。
- \(\texttt{afternoon(l,m,r)}\):给定环上当前节点的上一个,本身和下一个节点的被 \(\texttt{morning}\) 函数染上的颜色 \(l,m,r\),返回当前节点的新的颜色,颜色标号需在 \([0,10^9]\) 范围内。
- \(\texttt{evening(l,m,r)}\):给定环上当前节点的上一个,本身和下一个节点的被 \(\texttt{afternoon}\) 函数染上的颜色 \(l,m,r\),返回当前节点的新的颜色,颜色标号需在 \([0,40]\) 范围内。
环长至多 \(10^5\),实现的函数只能与函数形参即 \(l,m,r\) 有关。执行完三轮后,相邻两个节点的最终被 \(\texttt{evening}\) 函数染上的颜色应不同,且不同颜色数目应该尽可能小。设用了 \(k\) 种不同颜色,\(k=3\) 可以拿满分。
思路
这道题要求我们不断的去压缩环上颜色标号的最大值。注意到一开始和最终都满足相邻两点颜色不同,我们考虑维持相邻两点颜色不同的不变式。
\(k=6\)
我们考虑先实现一个环上相邻两个颜色的压缩函数 \(f(x,y)\),这个函数有定义域 \(x\neq y\),要保证 \(f(x,y)\neq f(y,z)\)。
人类智慧,考虑活用不变式 \(x\neq y\),我们找到二进制下 \(x,y\) 最高的不同位,然而只有这个信息很容易被 \(x=1,y=0,z=1\) 这种数据 hack 掉。
发现我们只需要再记录一个 \(x,y\) 的大小关系,那么如果 \(\text{highbit}(x \oplus y)=\text{highbit}(y \oplus z)\),一定有 \([x<y]\neq [y<z]\)。
在 \(\texttt{morning}\) 调用一次可以到 \(k=34\),再在 \(\texttt{afternoon}\) 调用一次 \(k=8\)。
注意到 \(\texttt{afternoon}\) 有三个参数,我们由于可以用 \(f(x,y)\neq f(y,z)\) 维持外层 \(f\) 的不变式,可以返回 \(f(f(x,y),f(y,z))\)。做到 \(k=6\),然而到了这种压缩方式的极限(\(2\lceil \log_2(n) \rceil\))了,所以 \(\texttt{evening}\) 相当于浪费。
\(k=4\)
考虑新的压缩方式,我们只要满足 \(f(x,y)\neq f(y,z)\),所以可以直接把值域中的每一个数压缩成长度为 \(T\) 只有 \(S\) 个 \(1\) 的 01 串。返回 \(x\) 为 \(1\) 而 \(y\) 为 \(0\) 的最高/低位
,由于 \(1\) 的个数相同所以这样的位一定存在。可以把 \({T\choose S}\) 的值域压到只有 \(T\)。
仿照上面的方式,可以一路把 \(k\) 压到 \(k=4\)。然而 \(k=4\) 就又到了极限了。
\(k=3\)
发现我们的 \(\texttt{evening}\) 啥都没干,我们考虑最后如何将 \(k=4\) 压到 \(k=3\)。
我们只修改颜色标号为 \(3\) 的位置,将它替换为 \(\neq l\) 且 \(\neq r\) 的颜色即可。
代码
#include "workshop.h"
void GospersHack(int n,int k,int *arr){
int cur=(1<<k)-1,cnt=0;
while(cur<(1<<n)){
arr[cnt++]=cur;
int r=cur+(cur&-cur);
cur=((r^cur)>>__builtin_ctz(cur)+2)|r;
}
}
int f1[184756],f2[20],f3[6];
void init(){
GospersHack(20,10,f1);
GospersHack(6,3,f2);
GospersHack(4,2,f3);
}
int F1(int x,int y){return __builtin_ctz(f1[x]&~f1[y]);}
int F2(int x,int y){return __builtin_ctz(f2[x]&~f2[y]);}
int F3(int x,int y){return __builtin_ctz(f3[x]&~f3[y]);}
int morning(int m,int r){return F1(m,r);}
int afternoon(int l,int m,int r){return F3(F2(l,m),F2(m,r));}
int evening(int l,int m,int r){
if(m<=2) return m;
for(int i=0;i<3;++i) if(i!=l&&i!=r) return i;
return -1;
}