跑步

题面

链接

懒得打字了,直接放图:

保证对于所有测试点,\(1\le 𝑛, \le 10^9, 1 \le 𝑚, 𝑘 \le 100,1 \le 𝑞 \le 10^5\), 𝑆中的所有元素都为\([1,10^9]\)范围内的整数,输入的𝑆中元素互不相同。

解法

这道题教我二分……

发现那个奇奇怪怪的f函数有个奇怪(显而易见)的性质,那就是它可以表示为 \(f(i)=i-x\) ,其中 \(x=|S\cap [1,i]|\),表述肯定不准确(毕竟本蒟蒻还不会集合),但大概就是这个意思。所以这个函数就可以理解为如果一次性跑完i公里的话,那么它就可以获得x的优惠。所以我们要求的就是优惠的最大值。

考虑到它的询问有点多,再加上它这个k又比较小,似乎可以预处理的样子。于是可以考虑对于每个k,预处理出用k次机会获得i个优惠的最少代价,然后询问时二分即可(顺便说一句,预处理的复杂度为 \(O(N^2K^2),算下来10^8\),但由于常数很小竟然可以卡过,大无语)。

预处理是很好写,但二分这里我傻了,硬是卡了很多次才发现问题。因为问题此时就变成了在一个有序序列里找到最后的一个数使得它小于等于n。然后不可思议的是,我在这里写挂了,别问我为什么,或许是我傻了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
#define zczc
using namespace std;
const int N=110;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar(); }
    wh*=f;return;
}
inline void check(int &s1,int s2){
	if(s1>s2)s1=s2;return;
}
inline int min(int s1,int s2){
	return s1<s2?s1:s2;
}
inline int max(int s1,int s2){
	return s1<s2?s2:s1;
}

int m,n,a[N],f[N][N*N];

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	read(m);
	for(int i=1;i<=m;i++)read(a[i]);
	sort(a+1,a+m+1);
	memset(f,0x3f,sizeof(f));
	//printf("%lld",f[0][0]);
	f[0][0]=0;
	for(int i=1;i<N;i++){
		f[i][0]=0;
		for(int j=1;j<=m*i;j++){
			check(f[i][j],f[i-1][j]);
			for(int k=min(j,m);k;k--){
				check(f[i][j],f[i-1][j-k]+a[k]);
			}
		}
	}
	int s1,s2;
	read(n);
	while(n--){
		read(s2);read(s1);
		int l=0,r=s1*m+2,mid;
		while(l<r){
			mid=l+r>>1;
			if(f[s1][mid]>s2)r=mid;
			else l=mid+1;
		}
		printf("%lld\n",s2-l+1);
	}
	
	return 0;
}
posted @ 2021-11-15 13:30  Feyn618  阅读(28)  评论(0编辑  收藏  举报