折半搜索【p4799】[CEOI2015 Day2]世界冰球錦標賽

Description

今年的世界冰球錦標賽在捷克舉行。Bobek 已經抵達布拉格,他不是任何團隊的粉絲,也沒有時間觀念。他只是單純的想去看幾場比賽。若是他有足夠的錢,他會去看全部的比賽。不幸的是,他的財產十分有限,他決定把全部財產都用來買門票。ios

給出 Bobek 的預算和每場比賽的票價,試求:若是總票價不超過預算,他有多少種觀賽方案。若是存在以其中一種方案觀看某場比賽而另外一種方案不觀看,則認爲這兩種方案不一樣。git

Input

第一行,兩個正整數 \(N\)\(M\)\((1 \leq N \leq 40,1 \leq M \leq 10^{18})\),表示比賽的個數和 Bobek 那家徒四壁的財產。數組

第二行,\(N\) 個以空格分隔的正整數,均不超過 \(10^{16}\),表明每場比賽門票的價格。spa

Output

輸出一行,表示方案的個數。因爲 \(N\) 十分大,注意:答案 \(\le 2^{40}\)code

顯然這個題直接dfs是過不去的\(O(2^n)\)ip

可是咱們能夠一半一半的搜,即折半搜索,複雜度能夠降到\(O(2^{\frac{n}{2}})\)get

因此咱們取一個\(mid\),分別搜前半段和後半段。input

而後合併答案的時候就須要令某一個數組變得有序,在其中找到最靠右的合法位置,直接累加便可。it

這裏用到了\(upper\)_\(bound\)io

代碼

#include<cstdio>
#include<iostream>
#include<algorithm>
#define R register
#define lo long long

using namespace std;

const int gz=1e6+6e5;

inline void in(R lo &x)
{
    R int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}

lo a[gz],b[gz],mon[42],ans,m;

int sum,cnt,n,mid;

void dfs(R int dep,R lo now)
{
    if(now>m)return;
    if(dep>mid)
    {
        a[++cnt]=now;
        return;
    }
    dfs(dep+1,now+mon[dep]);
    dfs(dep+1,now);
}

void dfss(R int dep,R lo now)
{
    if(now>m)return;
    if(dep>n)
    {
        b[++sum]=now;
        return;
    }
    dfss(dep+1,now+mon[dep]);
    dfss(dep+1,now);
}

int main()
{
    scanf("%d%lld",&n,&m);
    for(R int i=1;i<=n;i++)in(mon[i]);
    mid=(n+1)/2;
    dfs(1,0);dfss(mid+1,0);
    sort(b+1,b+sum+1);
    for(R int i=1;i<=cnt;i++)
        ans+=upper_bound(b+1,b+sum+1,m-a[i])-b-1;
    printf("%lld",ans);
}
相關文章
相關標籤/搜索