D2. RPD and Rap Sheet (Hard Version)
题目链接:https://codeforces.com/contest/1543/problem/D2
题意:
交互题,猜密码,设当前的密码为 \(x\) (不知道是多少)(保证初始密码在 0~n-1 之间),每次输入一个数 \(y\) 表示你猜的密码,若猜错,则密码变为 \(z,z\) 满足\({x}{\oplus_k}{z}=y\),我们将 \({\oplus_k}\) 定义为:\(a{\oplus_k}b=a\) 的 k 进制表示下的每一位与 \(b\) 的 k 进制下的对应位做模 k 加法之后得到的 k 进制数。
\(t\) 组测试样例,每组样例首先给定 \(n,k\) ,即你有 \(n\) 次猜密码的机会,每次猜错后密码 \(x\) 会变为 \(z,{x}{\oplus_k}{z}=y\),\(y\) 为你当前猜的密码。每次输出一个数来猜密码,若猜对则裁判程序会输入 1,然后直接进入下一组测试样例,若猜错则裁判程序会输入 0,继续猜直到猜对或达到次数限制。对于每组测试样例,若你都能在给定的 \(n\) 次次数限制内猜对密码,则通过。
思路:
对于上一题简单版本,我们通过模二加法和模二减法等价,且都等价于异或这个性质,在每一轮查询中使用 \(i\oplus(i-1)\) 匹配上了答案,这更像是猜出来的,因此我们现在来考虑通式。
现在我们来总体地考虑每一轮密码的通式,我们知道当我们当前查询为 y 且查询失败时,下一轮的密码 z 满足 \({x}{\oplus_k}{z}=y\),因为 \({x}{\oplus_k}{z}\) 是对 x 和 z 在转换成 k 进制之后对每一位做模 k 加法,根据模 k 加法和模 k 减法的性质,我们可以类似地定义 \({a}{\ominus_k}{a}\) 为 a 和 b 在转换成 k 进制之后对每一位做模 k 减法,这样由模加和模减的性质我们可以得到:\({x}{\oplus_k}{z}=y \rightarrow z=y{\ominus_k}x\),且易验证 \(\oplus_k\) 和 \(\ominus_k\) 与模加和模减一样满足交换律、结合律以及负负得正等特性,即下一轮密码就等于(当前我们猜测的密码 \(\ominus_k\) 当前密码),我们再设我们每一轮的查询为 \(q_i\),由此我们可以得到每一轮密码的通式:
| 轮数 | 1 | 2 | 3 | 4 | ... | n |
|---|---|---|---|---|---|---|
| 密码 | x | \(q_1{\ominus_k}x\) | \(q_2{\ominus_k}(q_1{\ominus_k}x)\) | \(q_3{\ominus_k}(q_2{\ominus_k}(q_1{\ominus_k}x))\) | ... | \(q_{n-1}{\ominus_k}(q_{n-2}{\ominus_k}...{\ominus_k}(q_2{\ominus_k}(q_1{\ominus_k}x))...)\) |
| 查询 | \(q_1\) | \(q_2\) | \(q_3\) | \(q_4\) | ... | \(q_n\) |
我们将负负得正的性质带入得:
| 轮数 | 1 | 2 | 3 | 4 | ... | n(n 为偶数) |
|---|---|---|---|---|---|---|
| 密码 | x | \(q_1{\ominus_k}x\) | \(q_2{\ominus_k}q_1{\oplus_k}x\) | \(q_3{\ominus_k}q_2{\oplus_k}q_1{\ominus_k}x\) | ... | \(q_{n-1}{\ominus_k}q_{n-2}{\oplus_k}...{\ominus_k}q_2{\oplus_k}q_1{\ominus_k}x\) |
| 查询 | \(q_1\) | \(q_2\) | \(q_3\) | \(q_4\) | ... | \(q_n\) |
| 轮数 | 1 | 2 | 3 | 4 | ... | n(n 为奇数) |
|---|---|---|---|---|---|---|
| 密码 | x | \(q_1{\ominus_k}x\) | \(q_2{\ominus_k}q_1{\oplus_k}x\) | \(q_3{\ominus_k}q_2{\oplus_k}q_1{\ominus_k}x\) | ... | \(q_{n-1}{\ominus_k}q_{n-2}{\oplus_k}...{\oplus_k}q_2{\ominus_k}q_1{\oplus_k}x\) |
| 查询 | \(q_1\) | \(q_2\) | \(q_3\) | \(q_4\) | ... | \(q_n\) |
我们得到了每一轮密码的通式,现在我们要构造的就是查询的通式,而我们的目标是:对于第 i=1~n 轮,每一轮我们都让 i-1 去匹配了 x,我们可以根据此目标来构造查询:对于第一轮,我们令\(q_1=0\),即第一轮用 0 去匹配了 x,然后后面每轮根据密码去构造,如下表所示:
| 轮数 | 1 | 2 | 3 | 4 | ... |
|---|---|---|---|---|---|
| 密码 | \(\color{red}{x}\) | \(0{\ominus_k}{\color{red}{x}}\) | \((0{\ominus_k}1){\ominus_k}0{\oplus_k}{\color{red}{x}}\) | \(((0{\ominus_k}1){\ominus_k}0{\oplus_k}2){\ominus_k}(0{\ominus_k}1){\oplus_k}0{\ominus_k}{\color{red}{x}}\) | ... |
| 查询 | \(\color{red}{0}\) | \(0{\ominus_k}{\color{red}{1}}\) | \((0{\ominus_k}1){\ominus_k}0{\oplus_k}{\color{red}{2}}\) | \(((0{\ominus_k}1){\ominus_k}0{\oplus_k}2){\ominus_k}(0{\ominus_k}1){\oplus_k}0{\ominus_k}{\color{red}{3}}\) | ... |
按运算律化简得:
| 轮数 | 1 | 2 | 3 | 4 | ... |
|---|---|---|---|---|---|
| 密码 | \({\color{red}{x}}\) | \(0{\ominus_k}{\color{red}{x}}\) | \({\color{red}{x}}{\ominus_k}1\) | \(2{\ominus_k}{\color{red}{x}}\) | ... |
| 查询 | \({\color{red}{0}}\) | \(0{\ominus_k}{\color{red}{1}}\) | \({\color{red}{2}}{\ominus_k}1\) | \(2{\ominus_k}{\color{red}{3}}\) | ... |
显然,我们可以得到查询的通式:
| 轮数 | 1 | 2 | 3 | 4 | ... | n |
|---|---|---|---|---|---|---|
| 密码 | \({\color{red}{x}}\) | \(0{\ominus_k}{\color{red}{x}}\) | \({\color{red}{x}}{\ominus_k}1\) | \(2{\ominus_k}{\color{red}{x}}\) | ... | \(\begin{cases} {{\color{red}{x}}{\ominus_k}(n-2)},&n为奇数 \\ {(n-2){\ominus_k}{\color{red}{x}}},&n为偶数 \end{cases}\) |
| 查询 | \({\color{red}{0}}\) | \(0{\ominus_k}{\color{red}{1}}\) | \({\color{red}{2}}{\ominus_k}1\) | \(2{\ominus_k}{\color{red}{3}}\) | ... | \(\begin{cases} {{\color{red}{(n-1)}}{\ominus_k}(n-2)},&n为奇数 \\ {(n-2){\ominus_k}{\color{red}{(n-1)}}},&n为偶数 \end{cases}\) |
模拟一下\({\ominus_k}\),按查询通式输出即可。
代码:
#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;
ll bitmodsub(ll a, ll b, ll k)
{
ll ans = 0, cur = 1;
while (a || b) //枚举位,然后进行模减,再累加到结果
{
ll t = (a % k - b % k + k) % k; //当前位模减
a /= k, b /= k;
ans += (t * cur); //答案加上当前位在k进制下的值
cur *= k;
}
return ans; //返回结果
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
ll t;
cin >> t;
while (t--)
{
ll n, k, r;
cin >> n >> k;
cout << 0 << endl;
cin >> r;
ll cnt = 1;
while (!r)
{
if (cnt & 1)
cout << bitmodsub(cnt - 1, cnt, k) << endl;
else
cout << bitmodsub(cnt, cnt - 1, k) << endl;
cnt++;
cin >> r;
}
}
}

浙公网安备 33010602011771号