描述
游戏世界中有 N N N个楼从左到右排列,从左到右编号为 1 1 1到 N N N,第 i i i幢楼的高度为 H i H_i Hi,楼上的金币数为 G i G_i Gi,游戏可以从任意一个楼开始且包涵几步。每一步玩家可以从当前位置向右跳(可以跳过一些楼)但必须跳到不低于当前楼的高度的楼上。他到了楼上后,可以得到楼上的金币。他可以在跳任意步(可以是零步)后结束游戏,但是要保证收到的金币数要大于等于K,现在想知道共有多少不同的种方案满足游戏。两个方案不同是指至少有一个楼不一样的方案。
输入
第一行两个数 N a n d K N and K NandK
接下来N行,每行两个正整数,第 i i i行用 H i H_i Hi和 G i G_i Gi表示第 i i i个楼的高度和上面的金币。
输出
一行一个数,表示方案总数。
范围
对于 40 % 40\% 40%的数据, n < = 20 n<=20 n<=20
对于 100 % 100\% 100%的数据, n < = 40 n<=40 n<=40
1 ≤ H i , G i ≤ 1 0 9 , 1 ≤ K ≤ 4 ⋅ 10 10 , 1 ≤ N ≤ 40 1 ≤ Hi, Gi ≤ 10^9 ,1 ≤ K ≤ 4·10^{10}, 1 ≤ N ≤ 40 1≤Hi,Gi≤109,1≤K≤4⋅1010,1≤N≤40
样例
样例输入1
4 6
2 1
6 3
7 2
5 6
样例输出1
3
样例输入2
2 7
4 6
3 5
样例输出2
0
样例输入3
4 15
5 5
5 12
6 10
2 1
样例输出3
4
看见 n ≤ 20 n \leq 20 n≤20,嗯 ~~ 爆搜可以过
看见 n ≤ 40 n \leq 40 n≤40,嗯 ~~ 折半爆搜无疑了
大体思路
我们在离散化之后将原数组分为左右两半,搜索后,最后把它们拼接起来,在操作中组成答案
细节化思路
在搜索左右两边的所有不下降子序列时,我们要存储它的金币数 v a l val val,方便后面计算。
同时,为了保证可以拼接起来,我们需要在搜左半部分时存储所有子序列的最后一个建筑的高度,在搜右半部分时存储所有子序列的第一个建筑的高度,满足后面比前面高才能拼接起来。
若左半部分的某个序列可以与右半部分的某个序列的 v a l val val加起来可以大于等于 k k k(没有判断能否拼接),那么右半部分的序列中 v a l val val比这个序列大的加起来都可以大于等于 k k k,所以,对于右半部分的序列,我们按 v a l val val,从小到大排序,省去一些冗杂的操作,排序后右半部分的 v a l val val是递增的,如果左半部分所有序列一一枚举的话会很耗费时间,所以我们想了尺取法,为了迎合右半部分的递增,我们就将左半部分的序列按 v a l val val从大到小排序。然后就开始尺取
当然,拼接后总有一些不符合条件的序列,因为我的尺取是用左半部分的序列去配对右半部分的序列,所以我用了个数组来存储右半部分中开头高度为 h h h的序列的可用个数,若当前序列与左半部分的某个序列 v a l val val之和小于k,那说明开头高度为 h h h的序列中有一个不可用了,就减减。若发现右边序列的开头还比左边序列的结尾还要小,那么所有开头高度为 h h h的序列都不可用, a n s ans ans减去所有开头高度为 h h h的序列的个数
#include
#include
#include
#include
using namespace std;
#define LL long long
int n, mid;
LL k, ans;
int h[45], g[45], temp[45], cnt[2];
LL lis[45];
struct node{
LL val;
int high;
node(){}
node(LL A, int hh){
val = A, high = hh;
}
}a[2][(1<<20)];
inline void dfs(LL v, int H, int i){
if( i > mid )
return ;
a[0][++cnt[0]] = node(v, H);
i++;
for(;i<=mid; i ++){
if( h[i] >= H )
dfs(v+g[i], h[i], i);
}
}
inline void dfs_(LL v, int first_h, int H, int i){
if( i > n )
return ;
a[1][++cnt[1]] = node(v, first_h);
i++;
for( ; i <= n; i ++){
if( h[i] >= H )
dfs_(v+g[i], first_h==0?h[i]:first_h, h[i], i);
}
}
IL bool cmp_1(node x, node y){
return x.val > y.val;
}
IL bool cmp_2(node x, node y){
return x.val < y.val;
}
int main(){
scanf("%d%lld", &n, &k);
for(int i = 1; i <= n; i ++){
scanf("%d%d", &h[i], &g[i]);
temp[i] = h[i];
}
sort(temp+1, temp+1+n);
int len = unique(temp+1,temp+1+n)-temp-1;
for(int i = 1; i <= n; i ++)
h[i] = lower_bound(temp+1, temp+1+len, h[i])-temp;
mid = (1+n)>>1;
dfs(0, 0, 0);
dfs_(0, 0, 0, mid);
sort(a[0]+1, a[0]+1+cnt[0], cmp_1);
sort(a[1]+1, a[1]+1+cnt[1], cmp_2);
a[1][1].high = 0;
for(int i = 1; i <= cnt[1]; i ++)
lis[a[1][i].high] ++;
int cl = 1, cr = 1;
while( cl <= cnt[0] ){
while( cr <= cnt[1] && a[0][cl].val+a[1][cr].val < k ){
lis[a[1][cr].high]--;
cr ++;
}
ans += cnt[1]-cr+1;
for(int i = 1; i <= n; i ++){
if( h[i] < a[0][cl].high )
ans-=lis[h[i]];
}
cl++;
}
printf("%lld\n", ans);
return 0;
}
/*
4 6
2 1
6 3
7 2
5 6
*/