通信题,小 A 和小 B 迷失在 \(4096\times 4096\) 的方阵中。
方阵是循环的,比如 \((0,4095)\) 的右边是 \((0,0)\),上面是 \((4095,4095)\)。两人都不知道自己的绝对位置。每一秒钟小 A 可以在他坐标系下的某个点打上标记,然后小 B 询问在他坐标系下 一行 或 一列 中标记个数。你需要尽快让小 B 知道在他坐标系下小 A 的坐标,所花时间为 \(x\),得分为 \(\min\{100,10 + \dfrac{13000}{x}\}\)。
\(x = 144\) 即可满分。
我们先考虑怎么求行,\(4096 = 64^2\),小 A 先将所有 \((64p, 64q)\) 的点打上标记。小 B 询问 \(0\sim 63\) 行/列即可知道最终坐标为 \((64p + a, 64q + b)\) 中的 \(a,b\)。相当于转化为 \(64 \times 64\) 的子问题。这个方法大概需要 \(256\) 秒。
开始填充标记需要 \(64\) 次,找到 \(a,b\) 各需要 \(64\) 次,我们可以将 填充和寻找放到一起,记得将询问和标记随机,这样期望在 \(128\) 次之内就找到。
但是对于 \(64\times 64\) 的子问题不可能用相同的方法。因为我们只剩最后 \(16\) 次操作可以进行。这恰好是一个倍增的复杂度。我们构造标记,使得 \(p,q\ge 32\) 的行/列,恰好每行/列 \(1\) 个标记,\(\ge16\) 的恰好 \(2\) 个标记,依此类推。我们要求的是第 \(0\) 行的坐标,这样每次询问当前行的标记数量,可以将规模减半,比如标记数量为 \(2\),说明 \(16\le x<32\),我们直接将 \(x - 16\)。
因为这个网站没有配置好通信题,所以下面代码的通信方式有点大病(
应该还有优化 / 更好的实现方法。下面这份代码代码分数在 \(99\) 附近波动。
下面是小 A 的代码
void alison(){
for(int i = 0; i < 4096; i += 64)mark(i, i);
int s = 32;
while(s > 1){
int p = s / 2;
rep(i, 0, s - 1)mark(p * 64, i * 64), p = (p + 1) % s;
s = p;
}
}
下面是小 B 的代码
int p[100], q[100];
std::mt19937 rd(7274482);
inline void su(int &x,int y){
x -= y; if(x < 0)x += 4096;
}
void bill(){
rep(i, 0, 63)p[i] = i + (rd() & 63) * 64, q[i] = i + (rd() & 63) * 64;
shuffle(p, p + 64, rd), shuffle(q, q + 64, rd);
int x = 0, y = 0, t = 0;
while(true){
++t;
if(numRow(p[t & 63])){x = p[t & 63]; break;}
}
while(true){
++t;
if(numColumn(q[t & 63])){y = q[t & 63]; break;}
}
while(t < 127)numRow(0), t++;
while(true){
int w = numRow(x);
if(w == 6)break;
su(x, (1 << (5 - w)) * 64);
}
if(numRow((x + 64) & 4095) != 6)su(x, 64);
while(true){
int w = numColumn(y);
if(w == 6)break;
su(y, (1 << (5 - w)) * 64);
}
if(numColumn((y + 64) & 4095) != 6)su(y, 64);
found(y, x);
}
标签:rd,标记,int,题解,Torusia,FARIO2013,while,63,64
From: https://www.cnblogs.com/7KByte/p/16602081.html