HDU 5439. Aggregated Counting (2015长春网络赛C题)

题目链接

赛后看了下别人的讨论恍然大悟。这题比赛的时候一直纠结着1e9的预处理会爆内存已经1e9的即时查询会tle。毕竟还是Too naive, 觉得数列题就只能有通项式, 或者是递推预处理, 完全没有想到预处理中间的过程采用公式能够大量节省时间以及空间Orz….

做法,首先这道题answer序列为 1, 5, 11, 23, 38, 62, 90… 经过找规律我们能发现ans[i] = Σ a[j] (1 <=a[j] <=i) 蓝儿这个规律比赛时早就推出来了, 却一直卡在了求和上。

下面是解法, 对于原数组a[i] = 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6…… 我们把每个数组最后一次出现的位置记作end[x], 比如end[1] = 1, end[2] = 3, end[3] = 5… 然后我们可以在原数组end[x - 1] + 1 ~ end[x]的位置的这个值 ( 对, 是值,比如end[2] + 1 ~ end[3]的位置的值∈{4, 5} ) 这个值在求和最后的答案中被累加了x次 ( 之前的出现在end[2] + 1 ~ end[3]中的4和5 累加的值就是 4+4+4+5+5+5 = (4 + 5) * 8 , 即 end[x - 1] + 1 ~ end[x] 等差数列求和 * x ) Orz… 因为差不多end[44w+] > 1e9, 于是这里的累加部分改成相乘 预处理复杂度直接从1e9变成了 40多w,Orrrrrrrrrrrrrrz ….

下面是代码

#include <bits/stdc++.h>

using namespace std;

const int p = 1000000007;

int a[500000];
int b[500000], k[500000];
vector <int> w;

long long get(int l, int r) {
  return 1ll * (l + r) * (r - l + 1) / 2;
}  // l到r的等差数列求和

int main() {
  a[1] = 1; a[2] = a[3] = 2;
  int M = 3;
  for (int i = 3;; i++) {
    for (int j = 0; j < a[i]; j++) {
      a[++M] = i;
    }
    if (M > 450000) break;
  }
  int x = 0, d = 1, c;
  long long ans = 0;
  w.push_back(0);   //这里的w就是end数组
  while (1) {
    c = a[d];
    ans = (ans + get(x + 1, x + c) * d) % p;
    x += c; d++; c = a[d];
    b[w.size()] = ans;
    k[w.size()] = d;
    w.push_back(x);
    if (x + c > p) break;  //题目中给的mod 1e9+7 差不多就和n的上限差不多 
  }
  k[0] = 1;
  int T;
  scanf("%d", &T);
  while (T--) {
    int n;
    scanf("%d", &n);
    int pos = lower_bound(w.begin(), w.end(), n) - w.begin() - 1;  
    //用二分求出比满足end[pos] < n 的最大的pos
    int x = w[pos]; long long ans = b[pos];
    if (x < n) ans = (ans + get(x + 1, n) * k[pos]) % p;
    cout << ans << endl;
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值