上次省选就有大佬提过折半查找,但是因为我过于蒟蒻,所以一直都没有去学习这种算法。
今天突发奇想,开了一发脑洞,学习一下这种算法。
这题的折半查找思想是非常好的,可以优化许多题目,可以把时间复杂度硬生生降一半。(好强的算法。)
折半查找,就是把数据分成两半,分别对它们进行操作,然后考虑这两半数据间的联系。(这TMD不是分治吗?)
对于这题来说,就是把物品分成两半,然后分别求可行的物品选取方案,然后考虑两组数据组合后的选择方案,两者相加就是答案。
附上AC代码:
#include <cstdio>
#include <cctype>
#include <algorithm>
#define N 41
#define M (1<<20)+10
using namespace std;
long long n,m,a[N],f[2][M],l,r,mid,ret,ans;
inline char nc(){
static char ch[100010],*p1=ch,*p2=ch;
return p1==p2&&(p2=(p1=ch)+fread(ch,1,100010,stdin),p1==p2)?EOF:*p1++;
}
inline void read(long long &a){
static char c=nc();int f=1;
for (;!isdigit(c);c=nc()) if (c=='-') f=-1;
for (a=0;isdigit(c);a=a*10+c-'0',c=nc());
a*=f;return;
}
inline void so(int x,int y,long long sum,int c){
if (sum>m) return;
if (x==y){
f[c][++f[c][0]]=sum;
return;
}
so(x+1,y,sum+a[x],c),so(x+1,y,sum,c);
return;
}
int main(void){
read(n),read(m);
for (int i=1; i<=n; ++i) read(a[i]);
so(1,(n>>1)+1,0,0),so((n>>1)+1,n+1,0,1);
sort(f[0]+1,f[0]+f[0][0]+1),sort(f[1]+1,f[1]+f[1][0]+1);
for (int i=1; i<=f[0][0]; ++i){
l=1,r=f[1][0],ret=0;
while (l<=r)
if (f[1][mid=(l+r)>>1]<=m-f[0][i]) ret=mid,l=mid+1;
else r=mid-1;
ans+=ret;
}
printf("%lld",ans);
return 0;
}
10.21
话说这题怎么变成权限题了?搞得我又要用YF大佬的号进去看题了……