|
|
|
@ -0,0 +1,135 @@
|
|
|
|
|
// 不要拷贝包信息的内容
|
|
|
|
|
package class39;
|
|
|
|
|
|
|
|
|
|
//优化版本
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
import java.util.Scanner;
|
|
|
|
|
|
|
|
|
|
public class Code02_SnacksWaysMain2 {
|
|
|
|
|
|
|
|
|
|
public static void main(String[] args) {
|
|
|
|
|
Scanner sc = new Scanner(System.in);
|
|
|
|
|
while (sc.hasNext()) {
|
|
|
|
|
size = sc.nextInt();
|
|
|
|
|
long w = (long) sc.nextInt();
|
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
|
arr[i] = (long) sc.nextInt();
|
|
|
|
|
}
|
|
|
|
|
long ways = ways(w);
|
|
|
|
|
System.out.println(ways);
|
|
|
|
|
}
|
|
|
|
|
sc.close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 用来收集所有输入的数字
|
|
|
|
|
public static long[] arr = new long[31];
|
|
|
|
|
public static int size = 0;
|
|
|
|
|
// 用来生成左部分可能的所有累加和
|
|
|
|
|
public static long[] leftSum = new long[1 << 16];
|
|
|
|
|
// 准备的数组可能用不完,左部分生成了多少累加和,用leftSize表示
|
|
|
|
|
public static int leftSize = 0;
|
|
|
|
|
// 用来生成右部分可能的所有累加和
|
|
|
|
|
public static long[] rightSum = new long[1 << 16];
|
|
|
|
|
// 准备的数组可能用不完,左部分生成了多少累加和,用leftSize表示
|
|
|
|
|
public static int rightSize = 0;
|
|
|
|
|
|
|
|
|
|
public static long ways(long w) {
|
|
|
|
|
if (size == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (size == 1) {
|
|
|
|
|
return arr[0] <= w ? 2 : 1;
|
|
|
|
|
}
|
|
|
|
|
// 求中点
|
|
|
|
|
int mid = size >> 1;
|
|
|
|
|
// 生成左侧的累加和
|
|
|
|
|
leftSize = 0;
|
|
|
|
|
dfsLeft(0, mid + 1, 0L);
|
|
|
|
|
// 生成右侧的累加和
|
|
|
|
|
rightSize = 0;
|
|
|
|
|
dfsRight(mid + 1, size, 0L);
|
|
|
|
|
// 把左侧累加和排序
|
|
|
|
|
Arrays.sort(leftSum, 0, leftSize);
|
|
|
|
|
// 把右侧累加和排序
|
|
|
|
|
Arrays.sort(rightSum, 0, rightSize);
|
|
|
|
|
// 解释一下,接下来的流程。
|
|
|
|
|
// 举个例子,比如:
|
|
|
|
|
// 左侧累加和是:{0, 1, 1, 1, 2, 2, 3, 4, 4}
|
|
|
|
|
// 右侧累加和是:{0, 1, 2, 3, 3, 3, 4, 4, 5}
|
|
|
|
|
// w = 5
|
|
|
|
|
// 左侧严格得到0的方法数:1
|
|
|
|
|
// 右侧得到<=5的方法数(二分求出):9
|
|
|
|
|
// 1 * 9
|
|
|
|
|
// 左侧严格得到1的方法数:3
|
|
|
|
|
// 右侧得到<=4的方法数(二分求出):8
|
|
|
|
|
// 3 * 8
|
|
|
|
|
// 左侧严格得到2的方法数:2
|
|
|
|
|
// 右侧得到<=3的方法数(二分求出):6
|
|
|
|
|
// 2 * 6
|
|
|
|
|
// 左侧严格得到3的方法数:1
|
|
|
|
|
// 右侧得到<=2的方法数(二分求出):3
|
|
|
|
|
// 1 * 3
|
|
|
|
|
// 左侧严格得到4的方法数:2
|
|
|
|
|
// 右侧得到<=1的方法数(二分求出):2
|
|
|
|
|
// 2 * 2
|
|
|
|
|
// 都累加起来
|
|
|
|
|
// 其实和课上讲的一样!多看一下例子
|
|
|
|
|
long ans = 0;
|
|
|
|
|
long count = 1;
|
|
|
|
|
for (int i = 1; i < leftSize; i++) {
|
|
|
|
|
if (leftSum[i] != leftSum[i - 1]) {
|
|
|
|
|
ans += count * (long) find(w - leftSum[i - 1]);
|
|
|
|
|
count = 1;
|
|
|
|
|
} else {
|
|
|
|
|
count++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ans += count * (long) find(w - leftSum[leftSize - 1]);
|
|
|
|
|
return ans;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 生成左部分的累加和,每一个累加和出来,都记录
|
|
|
|
|
public static void dfsLeft(int cur, int end, long sum) {
|
|
|
|
|
if (cur == end) { // 已经终止位置了
|
|
|
|
|
// 记录累加和
|
|
|
|
|
leftSum[leftSize++] = sum;
|
|
|
|
|
} else {
|
|
|
|
|
// 可能性1,不要当前数
|
|
|
|
|
dfsLeft(cur + 1, end, sum);
|
|
|
|
|
// 可能性2,要当前数
|
|
|
|
|
dfsLeft(cur + 1, end, sum + arr[cur]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 生成右部分的累加和,每一个累加和出来,都记录
|
|
|
|
|
public static void dfsRight(int cur, int end, long sum) {
|
|
|
|
|
if (cur == end) { // 已经终止位置了
|
|
|
|
|
// 记录累加和
|
|
|
|
|
rightSum[rightSize++] = sum;
|
|
|
|
|
} else {
|
|
|
|
|
// 可能性1,不要当前数
|
|
|
|
|
dfsRight(cur + 1, end, sum);
|
|
|
|
|
// 可能性2,要当前数
|
|
|
|
|
dfsRight(cur + 1, end, sum + arr[cur]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// <= num的数的个数,返回
|
|
|
|
|
public static int find(long num) {
|
|
|
|
|
int ans = -1;
|
|
|
|
|
int l = 0;
|
|
|
|
|
int r = rightSize - 1;
|
|
|
|
|
int m = 0;
|
|
|
|
|
while (l <= r) {
|
|
|
|
|
m = (l + r) / 2;
|
|
|
|
|
if (rightSum[m] <= num) {
|
|
|
|
|
ans = m;
|
|
|
|
|
l = m + 1;
|
|
|
|
|
} else {
|
|
|
|
|
r = m - 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ans + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|