2019暑假九考——San(COCI2017.2)——折半搜索+尺取

~目录~

        • 题目(1000ms)
        • 思路
        • Code


题目(1000ms)

描述
游戏世界中有 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 1Hi,Gi109,1K41010,1N40
样例

样例输入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 n20,嗯 ~~ 爆搜可以过
看见 n ≤ 40 n \leq 40 n40,嗯 ~~ 折半爆搜无疑了

大体思路
我们在离散化之后将原数组分为左右两半,搜索后,最后把它们拼接起来,在操作中组成答案

细节化思路
在搜索左右两边的所有不下降子序列时,我们要存储它的金币数 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的序列的个数


Code

#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
*/

你可能感兴趣的:(考试,尺取,题解,离散化)