你曾经梦想过你是电脑游戏中的主角吗?这个故事的主角,Branimir,现在正在做这个梦。
在Branimir的梦中,世界是由从左到右排列的N座摩天大楼组成的。对于第i座摩天大楼,我们知道摩天大楼的高度Hi和房顶金币的数量Gi。游戏从在任何摩天大楼上跳跃开始,由几步组成。在每一步中,Branimir都可以从他目前所在的摩天大楼向右跳(他也有可能跳过其中的几个),到一个高度不低于现在的摩天大楼。假如Branimir在一座摩天大楼,他可以拿这座大楼的金币。Branimir可以在任意步数之后结束游戏(0步也可以)通往下一关,但必须要收集至少K个金币。
现在要求Branimir通往下一关的方案数。两个方案当做不同当且仅当Branimir在其中一次跳过其中一座摩天大楼而另一次没有。
第一行包含两个整数n,K
接下来n行,每行两个整数Hi和Gi
输出一个整数,表示不同的方案数。
Sample Inputinput1:
4 6
2 1
6 3
7 2
5 6
input2:
2 7
4 6
3 5
input3:
4 15
5 5
5 12
6 10
2 1
output1:
3
output2:
0
output3:
4
对于40%的数据,1<=n<=20
对于100%的数据,1<=n<=40,1<=k<=4*10^10,1<=Hi,Gi<=10^9
第一个样例解释
{1,2,3},{1,4}{4}三种方案
正解
折半搜索
我们先搜出前面一半,存下他的两个信息(sl,hl),sl 是前面的金币和,hl 是最右端的点的高度。
我们先搜出后面一半,存下他的两个信息(sr,hr),sr 是后面的金币和,hr 是最左端的点的高度。
现在相当于找满足 sl+sr>=k,hl<=hr 的二元组的对数。
我们把 hl 相同的按照 sl 从小到大排序。
然后枚举二元组(sr,hr),接着枚举 hl(hl<=hr),然后就相当于求 sr+sl>=k 的个数,由于已经排序,sl 有序,所以后面只需要二分就行了。
水法
数据太水了
我用了A*+决策性剪枝就过了
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=107;
long long ans=0,k,f[maxn],h[maxn],g[maxn];
int n,cnt,list[maxn],hi[maxn];
struct E
{
int to,next;
}e[maxn*maxn];
void add(int u,int v)
{
e[++cnt].to=v; e[cnt].next=list[u]; list[u]=cnt;
}
long long get_h(int u)
{
long long s=g[u];
for(int i=list[u]; i; i=e[i].next) s=max(s,g[u]+h[e[i].to]);
return s;
}
long long get_f(int u)
{
long long s=1;
for(int i=list[u]; i; i=e[i].next) s+=f[e[i].to];
return s;
}
void dfs(int u,long long x)
{
if(x>=k)
{
ans+=f[u]; return;
}
if(x+h[u]-g[u]<k) return;
for(int i=list[u]; i; i=e[i].next)
{
int v=e[i].to;
dfs(v,x+g[v]);
}
return;
}
int main()
{
freopen("san.in","r",stdin); freopen("san.out","w",stdout);
scanf("%d%lld",&n,&k);
for(int i=1; i<=n; i++) scanf("%d%lld",&hi[i],&g[i]);
for(int i=1; i<n; i++)
{
for(int j=i+1; j<=n; j++) if(hi[i]<=hi[j]) add(i,j);
}
f[n]=1; h[n]=g[n];
for(int i=n-1; i>=1; i--) h[i]=get_h(i),f[i]=get_f(i);
for(int i=1; i<=n; i++) dfs(i,g[i]);
printf("%lld",ans);
return 0;
}