标签:
题意:给定一个含n个元素的序列,求下式子的结果。S(i,j)表示为seq[i...j]之和。注:对于log20可视为1。数据量n<=105。
思路:即使能够在O(1)的时间内求得任意S,也是需要O(n*n)来求和的。
对于这种题,一般就是研究式子,看有什么办法可以减少复杂度。
对于这个式子,S最大也不会超过longlong,确切计算,小于234。那么log的范围这么小,如果能够知道分别有多少个的话,那就快多了。可以看得出对于同一个i,log的结果是线性的,从1到34逐步递增的。那很好办,对于每个i,只需要将一整段给截出来,统计(i,j)之和再乘以logx即可。那么1~n最多可以截成34段啦。相比而言快了许多。复杂度为O(34*n)。
但是本题连这样的复杂度还是不行,还能继续优化,设为k,穷举k,再穷举i,对于每个i,假设i-1中(L,R)这一段的结果为k,而i的对应段大于等于这段。所以只需从上次穷举完之处继续判断即可。很难说清,看代码注释吧。
1 #include <bits/stdc++.h> 2 #define INF 0x7f7f7f7f 3 #define pii pair<int,int> 4 #define LL long long 5 using namespace std; 6 const int N=100100; 7 LL sum[N], up[50]; 8 int cur[N]; 9 void pre_cal() 10 { 11 sum[0]=0; 12 for(int i=0; i<40; i++) up[i]= (LL)1<<i; 13 } 14 LL cal(int n) 15 { 16 for(int i=0; i<=n; i++) cur[i]=i; //记录以i为下标的,穷举到那里了。 17 18 LL ans=0; 19 int L=1, R=1; 20 for(int k=0; k<35; k++) 21 { 22 R=cur[1]; 23 for(int i=1; i<=n; i++) //以i为下标的 24 { 25 L=cur[i]; 26 R=max(cur[i], cur[i-1]); //这一步决定了AC或者TLE 27 if(L>n) continue; //以i为下标的已经计算完毕。 28 29 while( R<=n && sum[R]-sum[i-1]<up[k] ) R++; //找到(logx+1)为k的一段[L,R) 30 if(L<R) 31 { 32 cur[i]=R; 33 if((R-L)&1) ans+=( (LL)(R-L)*i + (LL)(R-1+L)/2*(R-L) )*max(1,k); //注意这里千万要转longlong 34 else ans+=( (LL)(R-L)*i + (LL)(R-1+L)*(R-L)/2 )*max(1,k); 35 } 36 } 37 } 38 39 return ans; 40 } 41 42 int main() 43 { 44 freopen("input.txt", "r", stdin); 45 pre_cal(); 46 int t, n; 47 scanf("%d", &t); 48 while(t--) 49 { 50 scanf("%d", &n); 51 for(int i=1; i<=n; i++) 52 { 53 scanf("%lld", &sum[i]); 54 sum[i] += sum[i-1]; 55 } 56 printf("%lld\n", cal(n)); 57 } 58 return 0; 59 }
HDU 5358 First One 求和(序列求和,优化)
标签:
原文地址:http://www.cnblogs.com/xcw0754/p/4712257.html