hdu 5358 First One(想法题)

来源:互联网 发布:java泛型 pdf 编辑:程序博客网 时间:2024/06/05 00:44

题意:

给你如下的公式,要求你算出这个表达式的值。
ni=1nj=i(log2S(i,j)+1)×(i+j)
其中S(i,j)表示 i 到 j 的和。

解析:

由于(log2S(i,j)+1)的和表示的是:S(i,j)这个数字,转化成二进制的长度。
由于虽然连续的和可能比较大,但是其二进制的长度最多不会超过34位。
所以我们可以枚举,其二进制位数,并记为k。
然后查找满足区间[2k,2k1]的连续区间[l,r]
连续区域[l,r]求出后,那么[l,r]肯定满足一个等差数列,可以用等差数列的前n想和算出 (l+r1)(rl)/2,然后再加上(rl)i
那么最后答案就是 ((l+r1)(rl)/2+(rl)i)k

至于如何算出该区间,可以通过尺取法算出。
如果固定下一个端点i,延长j那么S(i,j)肯定的单调递增的。
可以通过这个单调性,用尺取法来算出这个区间的[l, r]的范围,总复杂度O(nlogn)

my code

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;typedef long long ll;const int N = 1e5 + 10;ll sum[N];int n;ll solve(ll L, ll R) {    ll ret = 0;    ll l = 0, r = 0;    for(ll i = 1; i <= n; i++) {        l = max(i, l);        r = max(i, r);        while(l <= n && sum[l] - sum[i-1] < L) l++;        while(r <= n && sum[r] - sum[i-1] < R) r++;        if(l >= r) continue;        ret += (r - l) * i + (l + r - 1) * (r - l) / 2;    }    return ret;}int main() {    int T;    scanf("%d", &T);    while(T--) {        scanf("%d", &n);        sum[0] = 0;        for(int i = 1; i <= n; i++) {            scanf("%lld", &sum[i]);            sum[i] += sum[i-1];        }        ll ans = 0, L, R;        for(ll k = 1; k <= 34; k++) {            L = 1LL << (k-1);            R = 1LL << k;            ans += k * solve(L, R);            if(L > sum[n]) break;        }        ans += solve(0, 1);        printf("%lld\n", ans);    }    return 0;}
0 0
原创粉丝点击