前言:初学者,查了各种资料,综合起来写了这篇文章
#include
包含了c++所有头文件,只要包含了这个头文件,可以随便引用所有自带的函数 memset重置清0整数数组(如int a[20];)==>memset(a,0,sizeof(a));
先写上const int N=2e5+5;然后接下来的数组直接写int a[N],b[N];
算法1 前缀和:sum[i]=sum[i-1]+a[i];在算出每个sum[i]之后,要想得到某个区间之和(不管是从1到i还是中间某个区间),可以快速查找
算法2 前缀最大值:c[i]=max(c[i-1],a[i]);求到i为止的最大值(用动态规划的思想,前一位的数已经算出最大值了,那么当前数大的最值就是前一位和本身比),同上,算出之后可以快速查找1到i的最值
算法3 二分查找(需要保证区间中的元素有序): 首先,stl有两个二分查找的函数,可以直接用
对于不降序数组:lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标
另外,写一个二分查找函数(此处以0到n-1为区间)(若以1到n为区间,则将l由0改为1,将r由len-1改为len)(在后面函数调用时,不管是从0开始还是从1开始,第一个参数都是写数组名,第三个参数写要查找的数,第二个参数一般写长度n就行了)
查找左边界的左一个:
int binsearch(int a[],int len,int val)
{
int l=0,r=len-1;
while(l
查找左边界:
int binsearch(int a[],int len,int val)
{
int l=0,r=len-1;
while(l
查找右边界:
int binsearch(int a[],int len,int val)
{
int l=0,r=len-1;
while(l
查找右边界的右一个:
int binsearch(int a[],int len,int val)
{
int l=0,r=len-1;
while(l
例1:Problem - E - Codeforces
[l,r]是要查找的区间,绝不会查找到区间以外,本题应该查找右边界的右一个并将右端点增加1。如果查找右边界,当要查找的数小于区间所有数时,最后会返回最开始区间最左端,当查找的数大于区间所有数,最后会返回最开始区间最右端,而题目需要的下标是最开始区间最左端的左一个(因为数组开在全局变量,又没有给c[0]赋值,所以c[0]是0,刚好符合需要)以及最开始区间最右端,所以统一不了。而查找右边界的右一个,当要查找的数小于区间所有数时,最后也返回最开始区间最左端,当要查找的数大于区间所有数时,最后也返回区间最右端,但是注意此时最右端是加了1的,因此,同时减1,就能满足题目所需下标,这也对应了upper_bound()函数
法一:用upper_bound()
#include
#include
using namespace std;
const int N=2e5+5;
int a[N],b[N],c[N];
long long sum[N];
int main()
{
int t,n,q,i,j;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&q);
for(i=1;i<=n;i++){
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
c[i]=max(c[i-1],a[i]);
}
for(i=1;i<=q;i++){
scanf("%d",&b[i]);
}
for(i=1;i<=q;i++){
j=upper_bound(c+1,c+n+1,b[i])-c;
printf("%lld ",sum[j-1]);
}
printf("\n");
memset(sum,0,sizeof(sum));
memset(c,0,sizeof(c));
}
return 0;
}
法二:自己写一个二分查找函数
#include
#include
using namespace std;
int binsearch(int arr[], int len, int val) {
int l = 1, r = len;
while (l < r) {
int m = l + ((r - l) / 2);
if (arr[m]<=val){
l = m + 1;
} else {
r = m;
}
}
return l;
}
const int N=2e5+5;
int a[N],b[N],c[N];
long long sum[N];
int main()
{
int t,n,q,i,j;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&q);
for(i=1;i<=n;i++){
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
c[i]=max(c[i-1],a[i]);
}
for(i=1;i<=q;i++){
scanf("%d",&b[i]);
}
for(i=1;i<=q;i++){
j=binsearch(c,n+1,b[i]);
printf("%lld ",sum[j-1]);
}
printf("\n");
memset(sum,0,sizeof(sum));
memset(c,0,sizeof(c));
}
return 0;
}
例2:https://codeforces.com/contest/1760/problem/F
思路:要找到最大的k(完成了一个任务之后的k天里不能再去做该任务)使得能够在d天里(一天只能完成一个任务)赚到至少c个金币,一共有n个任务,每个任务分配有不同的金币。当k越小时,比如k=0,那么我们可以在这些天每天做金币最高的任务,又比如k=1,我们在第一天做金币最高的任务,第二天做金币第二高的任务,然后第三天又做金币最高的任务,以此类推。首选金币高的任务做,当做不了的时候才选择做金币次高的任务,这样保证我们能获得的金币数尽可能大。k越小,我们可以重复做金币高的任务的次数更多,k越大,我们可以重复做金币高的任务的次数越少,而我们需要找的就是k最大能到多少也能赚到要求的金币(k小于这个最大值时赚到的金币更多)。
因此,我们需要去找到这么一个k值,如果有这么一个k值能使得获得金币数大于等于c,那么k的范围应在[0,d],因为若k大于d,那么在d天里,每天做的任务都不能重复,只能做一次,只能从金币数高的到低的任务一个一个做下来,做的任务数或天数就是n和d中小的那一个,如果有这么一个k值这样做的金币数大于等于c,则k可以无穷大,无论k多大,都是做这几个任务,而且只能做一次,所以在(k,正无穷)没有我们要找的k值。
sort对数组a降序,利于我们找金币个数高的,次高的,等等
利用sum[i]=sum[i-1]+a[i]求前缀和,利于我们找前i天最大金币数的总和
k最小为0,如果当k等于0时,此时每天选择金币最高的那个任务,若d*a[1]
当sum[min(n,d)]>=c,则Infinity
而要在[0,d]中找这么一个k值,如果遍历一个一个找,则时间复杂度太高,故选择用二分查找来查找k,利用二分查找,我们要对二分查找模板里的if判断语句进行修改,而且这里不是找边界了,不能完全照搬模板,当该k值满足时,则l=m,去看看是否还有更大的k值(不能写l=m+1,因为可能该k值已经是最大的了,因此要使满足的k仍在[l,r]区间里),当k不满足时,则r=m-1,此时的k太大了,则要去往小了找,且该k值不该在接下来的查找区间里出现了,则r=m-1而不是r=m
那么如何表示该k值是否满足呢:因为题目说完成了一个任务之后的k天里不能再去做该任务,那么实际上在k+1天里出现的每个任务不能重复,只能出现一次,将k+1与n进行比较,如果n小,那么在这k+1天里,只能做这n个任务一次,虽然还有余下的天数,但却什么都做不了;如果k+1小,那么在这k+1天里,将这k+1个任务做完,又从金币最高的任务开始重新做,又开始重新做这k+1个任务。然后用总天数d整除每个循环的任务数,就是要重复做多少个循环,即sum[min(k+1,n)]*(d/(k+1)),然后用总天数对k+1取余,得到剩下还有多少天,首先剩下的天数d%(k+1)肯定小于k+1,因为余数小于除数.若d%(k+1)小于n,则只要做d%(k+1)个任务就行了,若大于n,则也只能做n个任务了,因为在k+1天里任务不能重复,即sum[min(n,d%(k+1))],则
if(sum[min(m+1,n)]*(d/(m+1))+sum[min(n,d%(m+1))]>=c) 那么k值满足,否则不满足
#include
#include
using namespace std;
const int N=2e5+5;
long long a[N],sum[N],c;
int main()
{
int t,n,d,i,j,l,r,m;
cin>>t;
while(t--){
cin>>n>>c>>d;
for(i=1;i<=n;i++){
cin>>a[i];
}
sort(a+1,a+1+n,greater());
for(i=1;i<=n;i++){
sum[i]=sum[i-1]+a[i];
}
if(a[1]*d=c){
cout<<"Infinity"<=c)
l=m;
else
r=m-1;
}
cout<