P4799 [CEOI2015 Day2]世界冰球锦标赛
题目大意:有$n$个数,求任意选出若干个数,使其和不大于$m$的方案数。
暴力:每个数只有两种情况:选或者不选,然后再二进制判断计数。
优化:
将其分成两个集合$A$和$B$,其中$|A| = |B| = \dfrac{n}{2}$
$A$处理前$n$个数的方案个数并保留总和,$B$同理。
然后``two-pointers``。
时间复杂度大概是$O\left(2 ^ {\frac{n}{2}}\right)$,$n \le 40$可以过。
1 #include <bits/stdc++.h> 2 using namespace std; 3 int n; 4 long long m; 5 long long a[100]; 6 long long A[1 << 20 + 2], B[1 << 20 + 2]; 7 int Acnt = 0, Bcnt = 0; 8 long long ans = 0; 9 10 inline long long read(void) 11 { 12 long long s = 0; 13 int w = 1; 14 char ch = getchar(); 15 while (!isdigit(ch)) 16 { 17 if (ch == '-') 18 w = -1; 19 ch = getchar(); 20 } 21 while (isdigit(ch)) 22 { 23 s = s * 10 + ch - '0'; 24 ch = getchar(); 25 } 26 return s * w; 27 } 28 29 void dfs(int now, int nn, long long sum) 30 { 31 if (sum > m) 32 return; 33 if (now > nn) 34 { 35 if (nn == n) 36 A[++Acnt] = sum; 37 else 38 B[++Bcnt] = sum; 39 return; 40 } 41 dfs(now + 1, nn, sum); 42 dfs(now + 1, nn, sum + a[now]); 43 return; 44 } 45 46 int qfind(int R, int L) 47 { 48 int l = 0, r = L; 49 int ans = 0; 50 while (l <= r) 51 { 52 int mid = (l + r) >> 1; 53 if (A[mid] + B[R] <= m) 54 { 55 ans = mid; 56 l = mid + 1; 57 } 58 else 59 r = mid - 1; 60 } 61 return ans; 62 } 63 64 int main(void) 65 { 66 n = read(), m = read(); 67 for (int i = 1; i <= n; i++) 68 { 69 a[i] = read(); 70 } 71 dfs(1, n / 2, 0); 72 dfs(n / 2 + 1, n, 0); 73 sort(A + 1, A + Acnt + 1); 74 sort(B + 1, B + Bcnt + 1); 75 int l = Acnt, r = 1; 76 77 while (l >= 1 && r <= Bcnt) 78 { 79 l = qfind(r, l); 80 ans += l; 81 r++; 82 } 83 printf("%lld\n", ans); 84 return 0; 85 }