|
|
// 不要拷贝包信息的内容
|
|
|
package class39;
|
|
|
|
|
|
// 优化版本
|
|
|
// 这是牛客的测试链接:
|
|
|
// https://www.nowcoder.com/questionTerminal/d94bb2fa461d42bcb4c0f2b94f5d4281
|
|
|
// 请同学们务必参考如下代码中关于输入、输出的处理
|
|
|
// 这是输入输出处理效率很高的写法
|
|
|
// 提交如下的代码,并把主类名改成"Main"
|
|
|
// 可以直接通过
|
|
|
import java.io.BufferedReader;
|
|
|
import java.io.IOException;
|
|
|
import java.io.InputStreamReader;
|
|
|
import java.io.OutputStreamWriter;
|
|
|
import java.io.PrintWriter;
|
|
|
import java.io.StreamTokenizer;
|
|
|
import java.util.Arrays;
|
|
|
|
|
|
public class Code02_SnacksWaysMain2 {
|
|
|
|
|
|
// 用来收集所有输入的数字
|
|
|
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 void main(String[] args) throws IOException {
|
|
|
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
|
|
|
StreamTokenizer in = new StreamTokenizer(br);
|
|
|
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
|
|
|
while (in.nextToken() != StreamTokenizer.TT_EOF) {
|
|
|
size = (int) in.nval;
|
|
|
in.nextToken();
|
|
|
int bag = (int) in.nval;
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
in.nextToken();
|
|
|
arr[i] = (int) in.nval;
|
|
|
}
|
|
|
long ways = ways(bag);
|
|
|
out.println(ways);
|
|
|
out.flush();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
} |