题目
题目来源:CCF 安徽省选 2002;
评测地址:Luogu#2558
题目描述
在计算机网络中所有数据都是以二进制形式来传输的。但是在进行较大数据的传输时,直接使用该数的二进制形式加以传输则往往传输的位数过多。譬如要传输 (1024) 就需要 (11) 位二进制数。于是小可可提出了一种数据优化传输的设想,并打算对这一设想进行试验。
该设想是:正整数的所有方幂以及任意多个互不相等的 (k) 的方幂之和排成一个递增数列 (left<a_k ight>),例如当 (k=3) 时,(left<a_k ight>) 的前 (7) 项为 (1)(即 (3^0))、 (3)(即 (3^1))、(4)(即 (3^0+3^1))、(9) (即 (3^2))、(10)(即 (3^0+3^2))、(12)(即 (3^1+3^2))、(13)(即 (3^0+3^1+3^2))。
如果数 (d) 是数列 (left<a_k ight>) 中的第 (p) 项,则可以通过传送 (k) 和 (p) 这两个数来表示数 (d)。由于 (k) 和 (p) 这两个相对很小的数就可以表达出很大的数 ,因而理论上可以减少网络传输的位数。
小可可现在请你编写程序把接收到的数 (k) 和 (p) 所代表的数d计算出来。
输入格式
文件中以一行的形式存放了两个正整数 (k) 和 (p)。
输出格式
以一行的形式输出问题的解(解的位数不超过 (50) 位)。
数据规模及约定
评测时间限制 (1000 extrm{ms}),空间限制 (128 extrm{MiB})。
(1<kle 1024),(1le ple 1024)。
分析
题意是说给你一个由 (k^n) 的和构成的 递增 数列 (left<t ight>),求出这个数列的第 (p) 项。
观察这些和之间的大小关系,不难发现一个性质:(large{k^n>sumlimits_{i=1}^{n-1} k^i})
也就是说,所有加上了 (k^n) 的数必然比所有没有加上 (k^n) 的数(包括更大的数)要大。也就是说 (k^n) 的选与不选满足单调性。
我们定义 (p=sumlimits_{i} a_i imes 2^i),即 (left<a ight>) 是 (p) 的二进制表达。
只需证明 (t_p=sumlimits_{i} a_i imes k^i) 即可。
我们尝试用数学归纳法证明:初始,显然 (t_1=k)。
考虑 (t_v left(vin left[2^n,2^{n+1} ight) ight)),利用单调性,易得 (t_v) 必然选择了 (k^n)。
注意到这一段区间共有 (2^n) 个,而之前也有 (2^n) 个。根据单调性和数列定义,易得 (t_v=t_{v-2^n+1}+k^n),满足二进制。
证明了这个性质,接下来只需要对 (p) 进行二进制分解,然后预处理出 (k^n),最后遍历一遍就可以得到答案。
Code
别忘了加上高精度哦!
#include <cstdio>
#include <cstring>
using namespace std;
const int max_lgm = 12; // 最大的 n
struct long_int // 高精度(主要码量来源 hh)
{
typedef char B;
static const int max_n = 50;
B v[max_n+1]; // 奇怪的写法增加了(误
int len;
long_int() : len(0)
{
memset(v, 0, sizeof(v));
}
void assign(int n)
{
memset(v, 0, sizeof(v));
len = 0;
while (n > 0)
{
v[len] = n % 10;
n /= 10, len++;
}
}
void operator+=(const long_int& a)
{
for (int i = 0; i <= len || i <= a.len; i++)
{
v[i+1] += (v[i] + a.v[i]) / 10;
v[i] = (v[i] + a.v[i]) % 10;
}
if (a.len > len)
len = a.len;
while (!v[len])
len--;
len++;
}
void operator*=(const long_int& a)
{
B tmp[max_n+1] = {};
for (int i = 0; i < a.len; i++)
{
for (int j = 0; j < len; j++)
{
tmp[i+j+1] += (tmp[i+j] + a.v[i] * v[j]) / 10;
tmp[i+j] = (tmp[i+j] + a.v[i] * v[j]) % 10;
}
}
len += a.len;
while (!tmp[len])
len--;
len++;
for (int i = 0; i < len; i++)
v[i] = tmp[i];
}
void out()
{
for (int i = len - 1; i >= 0; i--)
putchar('0' + v[i]);
}
void outln()
{
out();
putchar('
');
}
void operator=(const long_int& a)
{
len = a.len;
for (int i = 0; i < len; i++)
v[i] = a.v[i];
}
};
long_int pw[max_lgm];
int main()
{
int p, k;
long_int tmp;
scanf("%d%d", &p, &k);
pw[0].assign(1); // 预处理
tmp.assign(p);
for (int i = 1; i < max_lgm; i++)
{
pw[i] = pw[i-1];
pw[i] *= tmp;
}
/*
tmp.outln();
for (int i = 0; i < max_lgm; i++)
pw[i].outln();
*/
tmp.assign(0);
for (int i = 1, j = 0; i <= k; i <<= 1, j++) // 二进制分解+统计答案
if (i & k)
tmp += pw[j];
tmp.outln(); // 精准结束
return 0; // 等待的就是 AC 喽
}