HDU 5358 First One 数学+尺取法
多校的题,摆明了数学题,但是没想出来,蠢爆了,之前算了半天的s[i][j]的和,其实是积。其实比赛的时候我连log(s[i][j])+1是s[i][j]的位数都没看出来,说出来都丢人。
知道了这个之后,就枚举二进制数的每一位,因为元素都是非负数,所以sum数组是非降的,这里用到了尺取法,之前也是听说过,应该是做过吧,不太记得了。
因为[2k-1,2k)的位数是k,枚举时,固定左端点,在sum数组找到最小的大于等于2k-1,最大的小于2k的点,这中间的点和左端点的s[i][j]就对于当前的k满足条件了,就把这些答案加到答案中,复杂度就是O(nlogn)
其实我的代码并没有AC,一直是超时,但是我做了一些极限的数据在本机上也是秒出,当然是我程序的原因,不过我现在还不知道怎么回事,花了这么多时间了,就这样吧。
哈哈原来是编译器的问题,之前交的C++,刚交了发G++就A了,
1 #include <iostream> 2 #include <cstdio> 3 #include <fstream> 4 #include <algorithm> 5 #include <cmath> 6 #include <deque> 7 #include <vector> 8 #include <queue> 9 #include <string> 10 #include <cstring> 11 #include <map> 12 #include <stack> 13 #include <set> 14 #define LL long long 15 #define INF 0x3f3f3f3f 16 #define OPEN_FILE 17 #define MAXN 100005 18 using namespace std; 19 LL sum[MAXN], p[MAXN]; 20 LL ans; 21 int main() 22 { 23 #ifdef OPEN_FILE 24 freopen("in.txt", "r", stdin); 25 //freopen("out.txt", "w", stdout); 26 #endif // OPEN_FILE 27 int T; 28 scanf("%d", &T); 29 p[0] = 0; 30 p[1] = 2; 31 for(int i = 2; i <= 34; i++){ 32 p[i] = p[i - 1] * 2; 33 } 34 for(int cas = 1; cas <= T; cas++){ 35 int n; 36 scanf("%d", &n); 37 memset(sum, 0, sizeof(sum)); 38 int x; 39 for(int i = 1; i <= n; i++){ 40 scanf("%d", &x); 41 sum[i] = sum[i - 1] + x; 42 } 43 ans = 0; 44 for(int i = 0; i <= 33; i++){ 45 LL left = 1, right = 1; 46 for(int j = 1; j <= n; j++){ 47 left = max((LL)j, left); 48 right = max((LL)j, right); 49 while(left <= n && sum[left] - sum[j - 1] < p[i]){ 50 left++; 51 } 52 //int right = left; 53 while(right <= n && sum[right] - sum[j - 1] < p[i + 1]){ 54 right++; 55 } 56 right--; 57 if(right > n){ 58 right = n; 59 } 60 if(left <= right){ 61 ans += ((((left + right)*(right - left + 1)) / 2) + (right - left + 1) * j) * (LL)(i + 1); 62 } 63 } 64 if(p[i + 1] > sum[n]){ 65 break; 66 } 67 } 68 printf("%I64d\n", ans); 69 } 70 }