HDU 5439 Aggregated Counting

传送门
找规律可以发现: ans=ia[i]i=1..n
即原数列中所有不超过 n 的数的和
容易发现一个数 x 最后出现位置即数列中前 x 项之和。
所以答案就是最后一个 n 的位置之前所有数的和。

#include <bits/stdc++.h>
using namespace std;
#define prt(k) cerr<<#k" = "<<k<<endl
typedef long long ll;
typedef long long LL;
const int N = 450100;
int a[N]; // a[i] : i出现次数
const LL mod = 1e9+7;
LL mul(LL a, LL n)
{
    a %= mod, n %= mod;
    LL r = 0;
    for (; n>0; n>>=1, a=(a+a)%mod) if (n&1) r=(r+a)%mod;
    return r;
}
ll pmod(ll a, ll n)
{
    ll r = 1; for (; n>0; n>>=1, a=a*a%mod) if (n&1) r=r*a%mod;
    return r;
}
int n, m;
LL dp[N];
int s[N];
int main()
{
    m = 0;
    a[++m] = 1;
    a[++m] = 2;
    a[++m] = 2;
    s[1] = 1;
    s[2] = 3;
    LL inv2 = pmod(2, mod-2);
    for (int n=3;m<N-1 && n<N;n++) {
        for (int i=0;m<N-1 && i<a[n];i++) {
            if (m == N-1) break;
            a[++m] = n;
        }
    }
    s[0] = 0;
    for (int i=1;i<N;i++) s[i]=s[i-1]+a[i];
    dp[0] = 0;
    for (int i=1;i<N;i++) {
        dp[i] = dp[i-1];
        LL t = mul(i, s[i-1] + s[i] + 1);
        t = mul(t, s[i] - s[i-1] + mod);
        t = mul(t, inv2);
        dp[i] = (dp[i] + t) % mod;
    }
    int re; cin>>re;
    while (re--) {
        scanf("%d", &n);
        int id  = upper_bound(s+1, s+N, n) - s - 1;
        LL ans = dp[id];
        LL t = mul(s[id] + n + 1, id + 1);
        t = mul(t, n - s[id] + mod);
        t = mul(t, inv2);
        ans = (ans + t) % mod;
        printf("%I64d\n", ans);
    }
    return 0;
}

你可能感兴趣的:(HDU 5439 Aggregated Counting)