这题以前做没有那么纠结,可能天气问题吧。。。
一个selfnumber是由前面的number“衍生”出来的,这个跟“合数是由前面的素数得到的”很相似,启发我们用类似素数筛子的方法来筛选出selfnumber
因此,可以用一个数组isSelfNumber[i]来标记 i是不是一个selfnumber, 同时,每找到一个selfnumber, 都将它丢进数组selfnumberList
但是, 这个方法有个最大的缺点, 就是空间复杂度是O(N)的, 当N = 10^7的时候, 必定会MLE.
首先容易想到, isSelfNumber[i] 可以用一个循环数组来实现, 因为一旦某个isSelfNumber[i]被扫描过, 以后就不需要它了.
比较巧妙的是对于回答后面的询问的优化.
由于询问的数量K是远远小于[1, N]之间的selfnumber的数量的, 所以, 其实我们不需要用selfnumberList来保存[1 ,N]之间的全部selfnumber, 我们只需要在每次找到一个selfnumber的时候, 都看一下这个selfnumber的序号是不是我们的那些询问中的一个.
受这个思路启发, 我开了一个数组sk[0..K-1], 这个数组的元素为三元组(c, w, n), c 为被询问的序号, w 为这个询问在输出中的位置, n是这个询问的答案
譬如说, 对于输入:
100 3
3 2 5
得到的sk数组是
[ (3, 0, ?), (2, 1, ?), (5, 2, ?) ]
然后, 按照c排序, 那么,当我们在做筛子操作的时候, 每次找到一个selfnumber, 我们都可以用二分查找在sk里判断是否存在一次对应的询问. 最后, 我们按照w排序, 就可以输出了.
这个方法看上去是可以行得通的, 可惜的是, 还是WA了. 原因在于, 这个方法忽略了一个东西: 询问可以有相同(重复)的.
于是, 我找回了自己以前写的解题报告, 发现这道题其实有个很简单的做法.
首先, 肯定是要把询问排序的. 但是, 当我们每找到一个selfnumber的时候, 我们其实不需要用二分查找, 因为被发现的selfnumber的序号是递增的, 而排序后的询问数组的c也是递增的, 所以, 我们用一个指针指向询问数组里的最小元素(c最小), 然后同时扫描1..N, 每次找到一个答案的时候, 我们就看看指针指向的迅问是不是对应于这个答案, 如果是的话, 我们就根据w字段把这个答案放到ansList的正确位置, 然后把指针指向下一个最小元素, 记得处理重复的情况.最后输出ansList就可以了.
当你需要输出若干次询问的答案, 将询问排序是关键, 并且你的算法是按照单调性一个个地找到答案的话, 都可以用本题里面的技巧.
在计算一个数的数字和, 有个小小的技巧, 类似于计算某个数的二进制表示里的1的个数. 我们先老老实实算出 10000以内的每个数的数字和, 并且把结果存起来, 放到dsum[0...10000]里面, 然后当我们要计算大于10000的数的数字和的时候, 我们可以将那个数字分成 低4位和高位 的数字和的和, 低4位的数字和可以直接拿前面的结果来用了, 然后高位可以递归算.
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
struct SelfNumSeq {
vector<char> a;
int head;
int headIndex;
void init(char value) {
a.resize(256);
head = 0;
headIndex = 0;
int i;
for (i = 0; i < a.size(); ++i) a[i] = value;
}
void set(int index, char value) {
a[(((index + 1) % a.size()) - 1 + a.size()) % a.size()] = value;
}
char get(int index) {
return a[(((index + 1) % a.size()) - 1 + a.size()) % a.size()];
}
void incHead(char value) {
a[head] = value;
head += 1;
if (head == a.size()) head = 0;
headIndex += 1;
}
};
// ==================================================
struct SKItem {
int count;
int w;
// int number;
};
SKItem newSKItem(int c, int w) {
SKItem sk;
sk.count = c;
sk.w = w;
// sk.number = n;
return sk;
};
bool cmpSKItem(const SKItem& a, const SKItem& b) {
return a.count < b.count;
}
bool cmpSKItemByW(const SKItem& a, const SKItem& b) {
return a.w < b.w;
}
// ================================================
// int bsear(vector<SKItem> &a, int start, int end, int v) {
// assert(end >= start);
// if (end - start + 1 <= 2) {
// int i;
// for (i = start; i <= end; ++i) if (a[i].count == v) return i;
// return -1;
// }
// int mid = start + (end - start) / 2;
// if ((a[mid].count) == v) return mid;
// if ((a[mid].count) < v) return bsear(a, mid + 1, end, v);
// return bsear(a, start, mid - 1, v);
// }
//===================================================
const char NOT_A_SELF_NUMBER = '\0';
const char IS_SELF_NUMBER = '\1';
SelfNumSeq selfNumSeq;
vector< SKItem > sk;
vector< int > ansList;
//====================================================
int ds(int x) {
int res = 0;
while (x > 0) {
res = res + x % 10;
x /= 10;
}
return res;
}
int main() {
int N, K;
int i;
int tmp;
scanf("%d %d", &N, &K);
sk.resize(0);
for (i = 0; i < K; ++i) {
scanf("%d", &tmp);
sk.push_back(newSKItem(tmp, i));
}
ansList.resize(sk.size());
sort(sk.begin(), sk.end(), cmpSKItem);
selfNumSeq.init(IS_SELF_NUMBER);
int shouldAnswerIndex = 0;
int count = 0;
for (i = 1; i <= N; ++i) {
if (selfNumSeq.get(i) == IS_SELF_NUMBER) {
++count;
while (1) {
if (shouldAnswerIndex >= sk.size()) break; // <= be careful!
if (count == sk[shouldAnswerIndex].count) {
ansList[sk[shouldAnswerIndex].w] = i;
shouldAnswerIndex++;
}
else
break;
}
// int b = bsear(sk, 0, sk.size() - 1, count);
// if (b != -1) {
// sk[b].number = i;
// sk[ sk[b].w ].number = i;
// }
}
int son = ds(i) + i;
if (son <= N) selfNumSeq.set(son, NOT_A_SELF_NUMBER);
selfNumSeq.incHead(IS_SELF_NUMBER);
}
printf("%d\n", count);
// sort(sk.begin(), sk.end(), cmpSKItemByW);
for (i = 0; i < ansList.size(); ++i) {
if (i != 0) printf(" ");
printf("%d", ansList[i]);
}
printf("\n");
return 0;
}