标签:数学题
链接 :http://acm.hdu.edu.cn/showproblem.php?pid=5358
1 2 1 1
12
题意:根据那个公式算出 ans。
做法:之前想着 枚举 左端点,再枚举 33个二进制数,再二分右端点来写。 O(33*n *log n) , t掉了。
这题卡时间卡得很严,看了题解后 ,发现有O(33*n)的做法。
显示枚举 33个二进制数, 然后取 num[i] num[j] 作为边界 L,R, 再枚举左端点,左端点遍历的时候 根据LR, 找到 右端点的范围l 和r 要求 L<=s(i,l)<=s(i,r)<R , 左端点不断右移,所以 边界l,r 也不断右移 不用回溯,所以是线性的复杂度为n。
c++还是会T g++可以过。
#include<map> #include<string> #include<cstring> #include<cstdio> #include<cstdlib> #include<cmath> #include<queue> #include<vector> #include<iostream> #include<algorithm> #include<bitset> #include<climits> #include<list> #include<iomanip> #include<stack> #include<set> using namespace std; #define ll __int64 ll num[40]; int aa[100100]; ll sum[100100]; int n; void init() { num[0]=0; num[1]=2; for(int i=2;i<38;i++) { num[i]=num[i-1]*2; } } ll solve(int c,ll L,ll R)//算 大于等于L 小于r的 { ll ans=0; int l,r; r=0; l=0; for(int i=1;i<=n;i++)//枚举起点 { if(r<i-1) r=i-1; if(l<i) l=i; while(l<n&&sum[l]-sum[i-1]<L) l++; while(r<n&&sum[r+1]-sum[i-1]<R) r++; if(r<l) continue; if(sum[l]-sum[i-1]<L) continue; if(sum[r]-sum[i-1]>=R) continue;; ans+=(ll)(c+1)*((ll)(l+r)*(ll)(r-l+1)/2+(ll)i*(ll)(r-l+1)); } return ans; } int main() { init(); int t; scanf("%d",&t); while(t--) { scanf("%d",&n); sum[0]=0; for(int i=1;i<=n;i++) { scanf("%d",aa+i); sum[i]=sum[i-1]+aa[i]; } __int64 ans=0; for(int i=0;i<=33;i++) { ans+=solve(i,num[i],num[i+1]); } printf("%I64d\n",ans); } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:数学题
原文地址:http://blog.csdn.net/u013532224/article/details/47339699