#A. 没出现的数字
- 传统题1000ms256MiB
Description
给你一个数字N,及Q
然后给你N个数字,Q代表有多少个询问
每个询问会给你一个数字x
询问你在正整数集合中,除开给你的这N个数字之外
第X个数字是多少。
Format
Input
第一行给出N,Q
第二行给出N个数字,权值<=1e18
接下来Q行,每行一个数字x
1<=N,Q<=1e5
x<=1e18
Output
如题目
Samples
输入数据 1
4 3
3 5 6 7
2
5
3
输出数据 1
2
9
4
二分枚举加在哪个读入数字的后面
统计出各个读入数字之前可以累计加多少个数字
#include<bits/stdc++.h>
using namespace std;
int n,q,po;
long long d[100002],sur[100002],x;//sur[i]:第i+1个数字前可加的数字
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
scanf("%lld",&d[i]);
sort(d+1,d+1+n);
sur[0]=d[1]-1;
for(int i=1;i<=n;i++)
sur[i]=sur[i-1]+d[i+1]-d[i]-1;
sur[n]=LLONG_MAX;
for(int i=1;i<=q;i++)
{
scanf("%lld",&x);
po=lower_bound(sur,sur+1+n,x)-sur;//大于或等于
printf("%lld\n",d[po]+x-sur[po-1]);
}
return 0;
}
#B. 数列分段II
- 传统题1000ms256MiB
Description
对于给定的一个长度为 N 的正整数数列 A ,现要将其分成M 段,并要求每段连续,且每段和的最大值最小。
例如,将数列4 2 4 5 1 要分成 3 段:
若分为(4,2),(4,5),(1) ,各段的和分别为6,9,1 ,和的最大值为9 ;
若分为4,(2,4),(5,1) ,各段的和分别为4,6,6 ,和的最大值为 6;
并且无论如何分段,最大值不会小于6 。
所以可以得到要将数列4 2 4 5 1 要分成3 段,每段和的最大值最小为6 。
Format
Input
第1 行包含两个正整数N,M;
第2 行包含N 个空格隔开的非负整数Ai ,含义如题目所述。
N<=1e5,M<=N
Ai之和可能超过1e9
Output
仅包含一个正整数,即每段和最大值最小为多少。
Samples
输入数据 1
5 3
4 2 4 5 1
输出数据 1
6
二分枚举答案
最大值与份数成负相关,份数>m-->最大值偏小;份数<m-->最大值偏大
#include<bits/stdc++.h>
using namespace std;
int n,m;
long long d[100002],l,r,mid,ans=LLONG_MAX;
bool check(long long mid)//错过
{
int pt=1;
long long sum=0;
for(int i=1;i<=n;i++)
if(sum+d[i]<=mid)
sum+=d[i];
else
{
sum=d[i];
pt++;
}
if(pt<=m)
return true;
return false;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&d[i]);
l=max(l,d[i]);//最小的最大值
r+=d[i];//最大的最大值
}
while(l<=r)
{
mid=(l+r)/2;
if(check(mid)==true)
{
ans=min(ans,mid);
r=mid-1;
}
else
l=mid+1;
}
printf("%lld",ans);
return 0;
}
坑点:long long和int,容易错
可以define int long long,虽然会占内存,但方便查错
#C. 砍树
- 传统题1000ms256MiB
Description
在一片树林里, 有 N 颗树,你需要砍下 M 米的木头.
仅有一排树木可以供你砍伐
伐木机的工作原理如下:
设置高度参数 H(以米为单位)
机器将一个巨大的锯片提升到那个高度,并切断高于 H 的所有树木部分
(当然,不高于 H 米的树木仍然完好无损).
例如:
如果有一排树木高度为 20,15,10 和 17 米,参数设为 15 米,
切割后剩余的树的 高度将是 15,15,10 和 15 米,得到的木头为第一棵树的 5 米和第四棵树的 2 米,总共 7 米。
因为提倡生态意识,所以不能砍下不必要的木头。
这就是为什么要把锯条尽可能地抬高。
请你找到最大的整数高度的锯片,使得你砍下至少 M 米的木头.
Format
Input
第一行包含两个空格分隔的正整数N,M ,含 义见题面.
第二行输入包含 N 个空格分隔的小于 1000000 的正整数.
第 i 个正整数表示第 i 棵的 高度.
保证所有树的高度和大于 M.
1≤N≤1 000 000
1≤M≤2 000 000 000
Output
一行一个正整数 H,即可以获得 M 米木材的前提下,设置锯片的最大高度
Samples
输入数据 1
4 7
20 15 10 17
输出数据 1
15
输入数据 2
3 7
10 8 5
输出数据 2
5
输入数据 3
3 6
10 8 5
输出数据 3
6
法一:二分+前缀和优化
#include<bits/stdc++.h>
using namespace std;
int n,m,d[1000002],ans,l,r,mid;
long long sur[1000002],tot,num,p;
signed main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&d[i]);
r=max(r,d[i]);
tot+=d[i];
}
sort(d+1,d+1+n);
for(int i=1;i<=n;i++)
sur[i]=sur[i-1]+d[i];
while(l<=r)
{
mid=(l+r)/2;
p=upper_bound(d+1,d+1+n,mid)-d-1;//upperbound找 大于 mid的第一个值
num=sur[p]+mid*(n-p);//1-p不被砍,加原长;p+1-n砍至mid
if(tot-num>=m)
{
ans=max(ans,mid);
l=mid+1;
}
else
r=mid-1;
}
printf("%d",ans);
return 0;
}
法二:贪心
每次让当前的树与前一棵树齐平,直到砍去总和大于等于m,再把多砍的均分给砍过的树
#include<bits/stdc++.h>
using namespace std;
int n,m,d[1000002],tot,ans;
bool cmp(int a,int b)
{
return b<a;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&d[i]);
sort(d+1,d+1+n,cmp);
for(int i=1;i<=n;i++)
{
tot+=i*(d[i]-d[i+1]);
if(tot>=m)
{
ans=d[i+1]+(tot-m)/i;
break;
}
}
printf("%d",ans);
return 0;
}
#D. 读书是最幸福的事
- 传统题1000ms256MiB
Description
学过编程都知道,程序=算法+数据结构
小J有两个书柜,一个全是算法书,另一个全是数据结构的书
小J在从这两个书柜中选书的时候,每个书柜都是总是从第一本书开始到某本书结束
现在小J有K个时间,现在他希望读尽可能多的书
Format
Input
第一行给N,M,K,分别代表两个书柜的书的数目及时间数
第二行给出N个数字,代表算法书的时间,其值<=1e9
第三行给出M个数字,代表数据结构书的时间,其值<=1e9
N,M<=2e5
K<=1e9
Output
如题
Samples
输入数据 1
3 4 240
60 90 120
80 150 80 150
输出数据 1
3
Hint
可以从第1个书柜中选前2本书
再从从第2个书柜中选前1本书
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,sp,j,ans;
long long d1[200002],d2[200002],x,k;
bool ok=false;
signed main()
{
scanf("%d%d%lld",&n,&m,&k);
for(int i=1;i<=n;i++)
{
scanf("%lld",&x);
d1[i]=x+d1[i-1];
if(d1[i]>k&&ok==false)
{
sp=i;
ok=true;
}
}
for(int i=1;i<=m;i++)
{
scanf("%lld",&x);
d2[i]=x+d2[i-1];
}
if(sp==0)
sp=n+1;
for(int i=0;i<=sp-1;i++)
{
j=upper_bound(d2+1,d2+1+m,k-d1[i])-d2-1;//upperbound找第一个大于k-d1[i]的,-1后小于或等于k-d1[i]
if(d1[i]+d2[j]<=k)
ans=max(ans,i+j);
}
printf("%d",ans);
return 0;
}
总结一下upper or lowerbound——在升序数组中——
lower_bound()查找的是大于等于输入数字的最靠左的位置
upper_bound()查找的是严格大于输入数字的最靠左的位置
lower_bound()-1查找的是严格小于输入数字的最靠右的位置
upper_bound()-1查找的是小于等于输入数字的最靠右的位置