HDU 5358(2015多校联合训练赛第六场1006) First One (区间合并+常数优化)

时间:2023-03-08 23:49:15
HDU 5358(2015多校联合训练赛第六场1006) First One (区间合并+常数优化)

pid=5358">HDU 5358

题意:

求∑​i=1​n​​∑​j=i​n​​(⌊log​2​​S(i,j)⌋+1)∗(i+j)。

思路:

S(i,j) < 10^10 < 2^34。所以log2(S)+1的值仅仅可能在1~35之间。因为log变化缓慢的函数特性,我们能够S(i,n)分作多个同样log值的区间来计算,用pos[i][j]预处理记录每一个以i为起点,log(s)值为j的区间的右边界。就可以优化至nlogn的复杂度。

主要是写起来比較难一些,一些细节比較纠结,一定思路理清后再写。

ps.此题卡常数毫无人性,一定记得预处理好区间映射,否则n(logn)^2也得跪。。

code:

/*
* @author Novicer
* language : C++/C
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
#define INF 2147483647
#define cls(x) memset(x,0,sizeof(x))
#define rise(i,a,b) for(int i = a ; i <= b ; i++)
using namespace std;
const double eps(1e-8);
typedef long long lint; const int maxn = 100000 + 5;
lint a[maxn];
lint pos[maxn][34];
lint two[34] = {2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,
268435456,536870912,1073741824,2147483648,4294967296,8589934592,17179869184}; int main(){
int t ; cin >> t ;
while(t--) {
int n; cin >> n;
for(int i = 1 ; i <= n ; i++){
scanf("%I64d",&a[i]);
}
for(int i = 0 ; i < 34 ; i++){
lint sum = a[1];
int right = 1;
for(int j = 1 ; j <= n ; j++){
if(j != 1) sum -= a[j-1];
while(sum < two[i] && right <= n)
sum += a[++right];
pos[j][i] = right;//以j为起点。log2(sum+1)值为i的区间右边界为right
}
}
lint ans = 0;
for(int i = 1 ; i <= n ; i++){
lint l = i;
lint r;
for(int j = 0 ; j < 34 ; j++){
r = pos[i][j];
ans += (1+j) * ((i) * (r-l) + (r+l-1)*(r-l)/2);//将同样log值区间的(i+j)累加起来
l = r;
}
}
cout << ans << endl; }
return 0;
}