原题解析
题目的大意为,给定一个长度 k [1, 64] 。
给定一个长度为 k 的字符串 s ,由 p 和 n 组成。
给定一个十进制数 n [-263, 263] 。
求一个二进制数 r ,使得 r 按照一个特殊的计算系统可以算得 n 。
特殊的计算系统
正常来说,将一个二进制数转化为十进制数时,需要对应的每一位 0 或 1 乘以系数,既对应位数 k 的 2k-1 ,再全部相加,形如:
0x11001 = 1 * 25-1 + 1 * 24-1 + 1 * 21-1 = 25
这里特殊的计算系统,在乘以系数时会根据位数 k 对应的字符串中的字符是 p 还是 n 来做出不同的操作。
当对应的字符是 p 时,系数没有特别的变化。
当对应的字符是 n 时,系数还要额外乘以 -1 。
举例:
s = ppnnn
0x11001 = 1 * 25-1 + 1 * 24-1 - 1 * 21-1 = 23
也就是说,这里会提供十进制数 23 ,而我们需要求出原本的 0x11001 。
解题思路
其实解法到这里已经呼之欲出,逆运算就行了。
按照之前的例子,在已知 0x11001 时要计算出 23 (0x10111) 时,会按位进行运算。
像这样运算:
第 1 位,0x11001 ppnnn ,得到 1 。
第 2 位,0x11001 ppnnn ,得到 1 * 2 + 1 = 3 。
第 3 位,0x11001 ppnnn ,得到 3 * 2 = 6 。
第 4 位,0x11001 ppnnn ,得到 6 * 2 = 12 。
第 5 位,0x11001 ppnnn ,得到 12 * 2 - 1 = 23 。
而反过来运算就是:
第 5 位, 23 ppnnn , 23 & 1 = 1 , 0xXXXX1 , (23 + 1) / 2 = 12 。
第 4 位, 12 ppnnn , 12 & 1 = 0 , 0xXXX01 , 12 / 2 = 6 。
第 3 位, 6 ppnnn , 6 & 1 = 0 , 0xXX001 , 6 / 2 = 3 。
第 2 位, 3 ppnnn , 3 & 1 = 1 , 0xX1001 , (3 - 1) / 2 = 1 。
第 1 位, 1 ppnnn , 1 & 1 = 1 , 0x11001 , (1 - 1) / 2 = 0 。
得到 0x11001 。
归纳过程:
- 遇 0 得 0,遇 1 得 1 ;
- 遇 1 时,遇 p 减 1,遇 n 加 1 ;(因为要除2,所以减 1 可以省略)
- 如果最后的结果不是 0 ,则说明原操作不成立,既 Impossible ;
示例代码
#include <stdio.h>
#include <string.h>
struct State
{
int k;
char s[65];
long long n;
char output[65];
};
void input(State& state);
void calc(State& state);
void output(State& state);
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
State state;
input(state);
calc(state);
output(state);
}
return 0;
}
void input(State& state)
{
scanf("%d", &(state.k));
scanf("%s", state.s);
scanf("%lld", &(state.n));
}
void calc(State& state)
{
long long n = state.n;
state.output[state.k] = 0;
for (int i = state.k - 1; i >= 0; --i)
{
if (n & 1)
{
if (state.s[i] == 'n')
{
n += 1;
}
state.output[i] = '1';
}
else
{
state.output[i] = '0';
}
n >>= 1;
}
if (n)
{
strcpy(state.output, "Impossible");
}
}
void output(State& state)
{
printf("%s\n", state.output);
}