1+ // 不要拷贝包信息的内容
2+ package class39 ;
3+
4+ //优化版本
5+ import java .util .Arrays ;
6+ import java .util .Scanner ;
7+
8+ public class Code02_SnacksWaysMain2 {
9+
10+ public static void main (String [] args ) {
11+ Scanner sc = new Scanner (System .in );
12+ while (sc .hasNext ()) {
13+ size = sc .nextInt ();
14+ long w = (long ) sc .nextInt ();
15+ for (int i = 0 ; i < size ; i ++) {
16+ arr [i ] = (long ) sc .nextInt ();
17+ }
18+ long ways = ways (w );
19+ System .out .println (ways );
20+ }
21+ sc .close ();
22+ }
23+
24+ // 用来收集所有输入的数字
25+ public static long [] arr = new long [31 ];
26+ public static int size = 0 ;
27+ // 用来生成左部分可能的所有累加和
28+ public static long [] leftSum = new long [1 << 16 ];
29+ // 准备的数组可能用不完,左部分生成了多少累加和,用leftSize表示
30+ public static int leftSize = 0 ;
31+ // 用来生成右部分可能的所有累加和
32+ public static long [] rightSum = new long [1 << 16 ];
33+ // 准备的数组可能用不完,左部分生成了多少累加和,用leftSize表示
34+ public static int rightSize = 0 ;
35+
36+ public static long ways (long w ) {
37+ if (size == 0 ) {
38+ return 0 ;
39+ }
40+ if (size == 1 ) {
41+ return arr [0 ] <= w ? 2 : 1 ;
42+ }
43+ // 求中点
44+ int mid = size >> 1 ;
45+ // 生成左侧的累加和
46+ leftSize = 0 ;
47+ dfsLeft (0 , mid + 1 , 0L );
48+ // 生成右侧的累加和
49+ rightSize = 0 ;
50+ dfsRight (mid + 1 , size , 0L );
51+ // 把左侧累加和排序
52+ Arrays .sort (leftSum , 0 , leftSize );
53+ // 把右侧累加和排序
54+ Arrays .sort (rightSum , 0 , rightSize );
55+ // 解释一下,接下来的流程。
56+ // 举个例子,比如:
57+ // 左侧累加和是:{0, 1, 1, 1, 2, 2, 3, 4, 4}
58+ // 右侧累加和是:{0, 1, 2, 3, 3, 3, 4, 4, 5}
59+ // w = 5
60+ // 左侧严格得到0的方法数:1
61+ // 右侧得到<=5的方法数(二分求出):9
62+ // 1 * 9
63+ // 左侧严格得到1的方法数:3
64+ // 右侧得到<=4的方法数(二分求出):8
65+ // 3 * 8
66+ // 左侧严格得到2的方法数:2
67+ // 右侧得到<=3的方法数(二分求出):6
68+ // 2 * 6
69+ // 左侧严格得到3的方法数:1
70+ // 右侧得到<=2的方法数(二分求出):3
71+ // 1 * 3
72+ // 左侧严格得到4的方法数:2
73+ // 右侧得到<=1的方法数(二分求出):2
74+ // 2 * 2
75+ // 都累加起来
76+ // 其实和课上讲的一样!多看一下例子
77+ long ans = 0 ;
78+ long count = 1 ;
79+ for (int i = 1 ; i < leftSize ; i ++) {
80+ if (leftSum [i ] != leftSum [i - 1 ]) {
81+ ans += count * (long ) find (w - leftSum [i - 1 ]);
82+ count = 1 ;
83+ } else {
84+ count ++;
85+ }
86+ }
87+ ans += count * (long ) find (w - leftSum [leftSize - 1 ]);
88+ return ans ;
89+ }
90+
91+ // 生成左部分的累加和,每一个累加和出来,都记录
92+ public static void dfsLeft (int cur , int end , long sum ) {
93+ if (cur == end ) { // 已经终止位置了
94+ // 记录累加和
95+ leftSum [leftSize ++] = sum ;
96+ } else {
97+ // 可能性1,不要当前数
98+ dfsLeft (cur + 1 , end , sum );
99+ // 可能性2,要当前数
100+ dfsLeft (cur + 1 , end , sum + arr [cur ]);
101+ }
102+ }
103+
104+ // 生成右部分的累加和,每一个累加和出来,都记录
105+ public static void dfsRight (int cur , int end , long sum ) {
106+ if (cur == end ) { // 已经终止位置了
107+ // 记录累加和
108+ rightSum [rightSize ++] = sum ;
109+ } else {
110+ // 可能性1,不要当前数
111+ dfsRight (cur + 1 , end , sum );
112+ // 可能性2,要当前数
113+ dfsRight (cur + 1 , end , sum + arr [cur ]);
114+ }
115+ }
116+
117+ // <= num的数的个数,返回
118+ public static int find (long num ) {
119+ int ans = -1 ;
120+ int l = 0 ;
121+ int r = rightSize - 1 ;
122+ int m = 0 ;
123+ while (l <= r ) {
124+ m = (l + r ) / 2 ;
125+ if (rightSum [m ] <= num ) {
126+ ans = m ;
127+ l = m + 1 ;
128+ } else {
129+ r = m - 1 ;
130+ }
131+ }
132+ return ans + 1 ;
133+ }
134+
135+ }
0 commit comments