【题解】[AHOI2002] 网络传输 By 5ab as a juruo.
题目
题目来源:CCF 安徽省选 2002;
评测地址:Luogu#2558
题目描述
在计算机网络中所有数据都是以二进制形式来传输的。但是在进行较大数据的传输时,直接使用该数的二进制形式加以传输则往往传输的位数过多。譬如要传输 \(1024\) 就需要 \(11\) 位二进制数。于是小可可提出了一种数据优化传输的设想,并打算对这一设想进行试验。
该设想是:正整数的所有方幂以及任意多个互不相等的 \(k\) 的方幂之和排成一个递增数列 \(\left<a_k\right>\),例如当 \(k=3\) 时,\(\left<a_k\right>\) 的前 \(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\right>\) 中的第 \(p\) 项,则可以通过传送 \(k\) 和 \(p\) 这两个数来表示数 \(d\)。由于 \(k\) 和 \(p\) 这两个相对很小的数就可以表达出很大的数 ,因而理论上可以减少网络传输的位数。
小可可现在请你编写程序把接收到的数 \(k\) 和 \(p\) 所代表的数d计算出来。
输入格式
文件中以一行的形式存放了两个正整数 \(k\) 和 \(p\)。
输出格式
以一行的形式输出问题的解(解的位数不超过 \(50\) 位)。
数据规模及约定
评测时间限制 \(1000\ \textrm{ms}\),空间限制 \(128\ \textrm{MiB}\)。
\(1<k\le 1024\),\(1\le p\le 1024\)。
分析
题意是说给你一个由 \(k^n\) 的和构成的 递增 数列 \(\left<t\right>\),求出这个数列的第 \(p\) 项。
观察这些和之间的大小关系,不难发现一个性质:\(\large{k^n>\sum\limits_{i=1}^{n-1} k^i}\)
也就是说,所有加上了 \(k^n\) 的数必然比所有没有加上 \(k^n\) 的数(包括更大的数)要大。也就是说 \(k^n\) 的选与不选满足单调性。
我们定义 \(p=\sum\limits_{i} a_i\times 2^i\),即 \(\left<a\right>\) 是 \(p\) 的二进制表达。
只需证明 \(t_p=\sum\limits_{i} a_i\times k^i\) 即可。
我们尝试用数学归纳法证明:初始,显然 \(t_1=k\)。
考虑 \(t_v\ \left(v\in \left[2^n,2^{n+1}\right)\right)\),利用单调性,易得 \(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('\n');
}
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 喽
}
本文来自博客园,作者 5ab,转载请注明链接哦 qwq
博客迁移啦,来看看新博客吧 -> https://5ab-juruo.oier.space/