长沙理工大学第十二届ACM大赛-重现赛 L - 选择困难症
题目描述
小L有严重的选择困难症。
早上起床后,需要花很长时间决定今天穿什么出门。
假设一共有k类物品需要搭配选择,每类物品的个数为Ai,每个物品有一个喜欢值Vj,代表小L对这件物品的喜欢程度。
小L想知道,有多少种方案,使得选出来的总喜欢值>M
需要注意,每类物品,至多选择1件,可以不选。
输入描述:
多组输入 每组数据第一行输入k M(k<=6,1<=M<=1e8),表示有多少类物品 接下来k行,每行以Ai(1<=Ai<=100)开头,表示这类物品有多少个,接下来Ai个数,第j个为Vj(1<=Vj<=1e8),表示小L对这类物品的第j个的喜欢值是多少。
输出描述:
每组输出一行,表示方案数
示例1
输入
2 5 3 1 3 4 2 2 3 2 1 2 2 2 2 2 2
输出
3 8
题解
折半搜索,二分。
物品分两堆,$[1,k/2]$一起处理,$[k/2+1,k]$一起处理。每一堆暴力处理出$100$万种选择的可能,然后枚举一边,二分另一边即可。
#include <bits/stdc++.h> using namespace std; int k; long long m; long long v[10][200]; int a[10]; long long p[2][1100000]; int sz0, sz1; void dfs0(int x, long long y) { if(x == k / 2 + 1) { p[0][sz0 ++] = y; return; } for(int i = 0; i <= a[x]; i ++) { dfs0(x + 1, y + v[x][i]); } } void dfs1(int x, long long y) { if(x == k + 1) { p[1][sz1 ++] = y; return; } for(int i = 0; i <= a[x]; i ++) { dfs1(x + 1, y + v[x][i]); } } int main() { while(~scanf("%d%lld", &k, &m)) { for(int i = 1; i <= k; i ++) { scanf("%d", &a[i]); for(int j = 1; j <= a[i]; j ++) { scanf("%lld", &v[i][j]); } } if(k == 1) { int sum = 0; for(int j = 1; j <= a[1]; j ++) { if(v[1][j] > m) sum ++; } printf("%d\n", sum); continue; } sz0 = sz1 = 0; dfs0(1, 0); dfs1(k / 2 + 1, 0); sort(p[1], p[1] + sz1); long long ans = 0; for(int i = 0; i < sz0; i ++) { int L = 0, R = sz1 - 1, pos = sz1; while(L <= R) { int mid = (L + R) / 2; if(p[0][i] + p[1][mid] > m) pos = mid, R = mid - 1; else L = mid + 1; } ans = ans + (sz1 - pos); } printf("%lld\n", ans); } return 0; }