sgu 108

这题以前做没有那么纠结,可能天气问题吧。。。

一个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;

}



转载于:https://my.oschina.net/mustang/blog/55779

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值