【深基13.例1】查找
题目描述
输入 \(n\) 个不超过 \(10^9\) 的单调不减的(就是后面的数字不小于前面的数字)非负整数 \(a_1,a_2,\dots,a_{n}\),然后进行 \(m\) 次询问。对于每次询问,给出一个整数 \(q\),要求输出这个数字在序列中第一次出现的编号,如果没有找到的话输出 \(-1\) 。
输入格式
第一行 \(2\) 个整数 \(n\) 和 \(m\),表示数字个数和询问次数。
第二行 \(n\) 个整数,表示这些待查询的数字。
第三行 \(m\) 个整数,表示询问这些数字的编号,从 \(1\) 开始编号。
输出格式
输出一行,\(m\) 个整数,以空格隔开,表示答案。
样例 #1
样例输入 #1
11 3
1 3 3 3 5 7 9 11 13 15 15
1 3 6
样例输出 #1
1 2 -1
提示
数据保证,\(1 \leq n \leq 10^6\),\(0 \leq a_i,q \leq 10^9\),\(1 \leq m \leq 10^5\)
本题输入输出量较大,请使用较快的 IO 方式。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
void calc(int q,vector<int> &a,int n)
{
int L=0,R=n+1;
while(L+1<R)
{
int M=(L+R)>>1;
if(a[M]<q) L=M;
else R=M;
}
if(a[R]==q) cout<<R<<' ';
else cout<<"-1 ";
}
void solve()
{
int n,m;
cin>>n>>m;
vector<int> a(n+1);
for(int i=1;i<=n;++i) cin>>a[i];
while(m--)
{
int q;
cin>>q;
calc(q,a,n);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
A-B 数对
题目背景
出题是一件痛苦的事情!
相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的 A+B Problem,改用 A-B 了哈哈!
题目描述
给出一串正整数数列以及一个正整数 \(C\),要求计算出所有满足 \(A - B = C\) 的数对的个数(不同位置的数字一样的数对算不同的数对)。
输入格式
输入共两行。
第一行,两个正整数 \(N,C\)。
第二行,\(N\) 个正整数,作为要求处理的那串数。
输出格式
一行,表示该串正整数中包含的满足 \(A - B = C\) 的数对的个数。
样例 #1
样例输入 #1
4 1
1 1 2 3
样例输出 #1
3
提示
对于 \(75\%\) 的数据,\(1 \leq N \leq 2000\)。
对于 \(100\%\) 的数据,\(1 \leq N \leq 2 \times 10^5\),\(0 \leq a_i <2^{30}\),\(1 \leq C < 2^{30}\)。
2017/4/29 新添数据两组
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int a[N];
void solve()
{
int n,c;
cin>>n>>c;
for(int i=0;i<n;++i) cin>>a[i];
sort(a,a+n);
long long res=0;
for(int i=0;i<n;++i)
res+=(upper_bound(a,a+n,a[i]+c)-lower_bound(a,a+n,a[i]+c));
cout<<res<<'\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
[COCI2011-2012#5] EKO / 砍树
题目描述
伐木工人 Mirko 需要砍 \(M\) 米长的木材。对 Mirko 来说这是很简单的工作,因为他有一个漂亮的新伐木机,可以如野火一般砍伐森林。不过,Mirko 只被允许砍伐一排树。
Mirko 的伐木机工作流程如下:Mirko 设置一个高度参数 \(H\)(米),伐木机升起一个巨大的锯片到高度 \(H\),并锯掉所有树比 \(H\) 高的部分(当然,树木不高于 \(H\) 米的部分保持不变)。Mirko 就得到树木被锯下的部分。例如,如果一排树的高度分别为 \(20,15,10\) 和 \(17\),Mirko 把锯片升到 \(15\) 米的高度,切割后树木剩下的高度将是 \(15,15,10\) 和 \(15\),而 Mirko 将从第 \(1\) 棵树得到 \(5\) 米,从第 \(4\) 棵树得到 \(2\) 米,共得到 \(7\) 米木材。
Mirko 非常关注生态保护,所以他不会砍掉过多的木材。这也是他尽可能高地设定伐木机锯片的原因。请帮助 Mirko 找到伐木机锯片的最大的整数高度 \(H\),使得他能得到的木材至少为 \(M\) 米。换句话说,如果再升高 \(1\) 米,他将得不到 \(M\) 米木材。
输入格式
第 \(1\) 行 \(2\) 个整数 \(N\) 和 \(M\),\(N\) 表示树木的数量,\(M\) 表示需要的木材总长度。
第 \(2\) 行 \(N\) 个整数表示每棵树的高度。
输出格式
\(1\) 个整数,表示锯片的最高高度。
样例 #1
样例输入 #1
4 7
20 15 10 17
样例输出 #1
15
样例 #2
样例输入 #2
5 20
4 42 40 26 46
样例输出 #2
36
提示
对于 \(100\%\) 的测试数据,\(1\le N\le10^6\),\(1\le M\le2\times10^9\),树的高度 \(<10^9\),所有树的高度总和 \(>M\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int a[N];
int n,m;
long long calc(int x)
{
long long res=0;
for(int i=1;i<=n;++i) if(a[i]>x) res+=a[i]-x;
return res;
}
void solve()
{
//int n,m;
cin>>n>>m;
for(int i=1;i<=n;++i) cin>>a[i];
int L=-1,R=1e9;
while(L+1<R)
{
int M=L+(R-L)/2;
//cout<<calc(M)<<'\n';
if(calc(M)>=m) L=M;
else R=M;
}
cout<<L<<'\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
[NOIP2001 提高组] 一元三次方程求解(牛顿迭代)
题目描述
有形如:\(a x^3 + b x^2 + c x + d = 0\) 这样的一个一元三次方程。给出该方程中各项的系数(\(a,b,c,d\) 均为实数),并约定该方程存在三个不同实根(根的范围在 \(-100\) 至 \(100\) 之间),且根与根之差的绝对值 \(\ge 1\)。要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后 \(2\) 位。
提示:记方程 \(f(x) = 0\),若存在 \(2\) 个数 \(x_1\) 和 \(x_2\),且 \(x_1 < x_2\),\(f(x_1) \times f(x_2) < 0\),则在 \((x_1, x_2)\) 之间一定有一个根。
输入格式
一行,\(4\) 个实数 \(a, b, c, d\)。
输出格式
一行,\(3\) 个实根,从小到大输出,并精确到小数点后 \(2\) 位。
样例 #1
样例输入 #1
1 -5 -4 20
样例输出 #1
-2.00 2.00 5.00
提示
【题目来源】
NOIP 2001 提高组第一题
点击查看代码
#include<bits/stdc++.h>
using namespace std;
double a,b,c,d;
double f(double x)
{
return a*x*x*x+b*x*x+c*x+d;
}
double df(double x)
{
return 3*a*x*x+2*b*x+c;
}
void calc(double l,double r)
{
double x0=(l+r)/2;
double x;
while(fabs(f(x0))>1e-6)
{
x=x0-f(x0)/df(x0);
x0=x;
}
cout<<fixed<<setprecision(2)<<x0<<' ';
}
void solve()
{
cin>>a>>b>>c>>d;
double p=(-b-sqrt(b*b-3*a*c))/(3*a);
double q=(-b+sqrt(b*b-3*a*c))/(3*a);
calc(-100,p);
calc(p,q);
calc(q,100);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
烦恼的高考志愿
题目背景
计算机竞赛小组的神牛 V 神终于结束了高考,然而作为班长的他还不能闲下来,班主任老 t 给了他一个艰巨的任务:帮同学找出最合理的大学填报方案。可是 v 神太忙了,身后还有一群小姑娘等着和他约会,于是他想到了同为计算机竞赛小组的你,请你帮他完成这个艰巨的任务。
题目描述
现有 \(m\) 所学校,每所学校预计分数线是 \(a_i\)。有 \(n\) 位学生,估分分别为 \(b_i\)。
根据 \(n\) 位学生的估分情况,分别给每位学生推荐一所学校,要求学校的预计分数线和学生的估分相差最小(可高可低,毕竟是估分嘛),这个最小值为不满意度。求所有学生不满意度和的最小值。
输入格式
第一行读入两个整数 \(m,n\)。\(m\) 表示学校数,\(n\) 表示学生数。
第二行共有 \(m\) 个数,表示 \(m\) 个学校的预计录取分数。第三行有 \(n\) 个数,表示 \(n\) 个学生的估分成绩。
输出格式
输出一行,为最小的不满度之和。
样例 #1
样例输入 #1
4 3
513 598 567 689
500 600 550
样例输出 #1
32
提示
数据范围:
对于 \(30\%\) 的数据,\(1\leq n,m\leq1000\),估分和录取线 \(\leq10000\);
对于 \(100\%\) 的数据,\(1\leq n,m\leq100000\),估分和录取线 \(\leq 1000000\) 且均为非负整数。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int m,n;
int a[N];//a是分数线,b是估分
int calc(int x)
{
int L=0,R=m+1;
while(L+1<R)
{
int M=(L+R)>>1;
if(a[M]<=x) L=M;
else R=M;
}
if(!L) return a[1]-x;
if(R==m+1) return x-a[m];
return min(x-a[L],a[R]-x);
}
void solve()
{
cin>>m>>n;
for(int i=1;i<=m;++i) cin>>a[i];
sort(a+1,a+m+1);
ll res=0;
for(int i=1;i<=n;++i)
{
int b;
cin>>b;
res+=calc(b);
}
cout<<res<<'\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
木材加工
题目背景
要保护环境
题目描述
木材厂有 \(n\) 根原木,现在想把这些木头切割成 \(k\) 段长度均为 \(l\) 的小段木头(木头有可能有剩余)。
当然,我们希望得到的小段木头越长越好,请求出 \(l\) 的最大值。
木头长度的单位是 \(\text{cm}\),原木的长度都是正整数,我们要求切割得到的小段木头的长度也是正整数。
例如有两根原木长度分别为 \(11\) 和 \(21\),要求切割成等长的 \(6\) 段,很明显能切割出来的小段木头长度最长为 \(5\)。
输入格式
第一行是两个正整数 \(n,k\),分别表示原木的数量,需要得到的小段的数量。
接下来 \(n\) 行,每行一个正整数 \(L_i\),表示一根原木的长度。
输出格式
仅一行,即 \(l\) 的最大值。
如果连 \(\text{1cm}\) 长的小段都切不出来,输出 0。
样例 #1
样例输入 #1
3 7
232
124
456
样例输出 #1
114
提示
数据规模与约定
对于 \(100\%\) 的数据,有 \(1\le n\le 10^5\),\(1\le k\le 10^8\),\(1\le L_i\le 10^8(i\in[1,n])\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n,k;
int a[N];
ll calc(int x)
{
ll res=0;
for(int i=1;i<=n;++i) res+=a[i]/x;
return res;
}
void solve()
{
cin>>n>>k;
ll sum=0;
int maxn=0;
for(int i=1;i<=n;++i)
{
cin>>a[i];sum+=a[i];maxn=max(maxn,a[i]);
}
if(sum<k) {cout<<"0\n";return ;}
int L=1,R=maxn+1;
while(L+1<R)
{
int M=L+(R-L)/2;
if(calc(M)>=k) L=M;
else R=M;
}
cout<<L<<'\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
[NOIP2015 提高组] 跳石头
题目背景
一年一度的“跳石头”比赛又要开始了!
题目描述
这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 \(N\) 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。
为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 \(M\) 块岩石(不能移走起点和终点的岩石)。
输入格式
第一行包含三个整数 \(L,N,M\),分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。保证 \(L \geq 1\) 且 \(N \geq M \geq 0\)。
接下来 \(N\) 行,每行一个整数,第 \(i\) 行的整数 \(D_i( 0 < D_i < L)\), 表示第 \(i\) 块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。
输出格式
一个整数,即最短跳跃距离的最大值。
样例 #1
样例输入 #1
25 5 2
2
11
14
17
21
样例输出 #1
4
提示
输入输出样例 1 说明
将与起点距离为 \(2\)和 \(14\) 的两个岩石移走后,最短的跳跃距离为 \(4\)(从与起点距离 \(17\) 的岩石跳到距离 \(21\) 的岩石,或者从距离 \(21\) 的岩石跳到终点)。
数据规模与约定
对于 \(20\%\)的数据,\(0 \le M \le N \le 10\)。
对于 \(50\%\) 的数据,\(0 \le M \le N \le 100\)。
对于 \(100\%\)的数据,\(0 \le M \le N \le 50000,1 \le L
\le 10^9\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10,suf=0x3f3f3f3f;
int a[N];
int l,n,m;
//用calc(x)表示以x为最短跳跃距离需要移去的石子数,则函数calc是单调递增的
//那么一定存在一个分界点使得界限左边的都小于等于m界限右边的都大于m
int calc(int x)
{
int res=0;
int des=0;
int flag=0;
for(int i=1;i<=n+1;++i)
{
for(int j=i;j<=n+1;++j)
{
flag=j;
if(a[j]-des>=x) break;
if(j==n+1) {return res+1;}
//注意最后有可能达不到终点需要再额外移去一块
}
res+=flag-i;
i=max(i,flag);
des=a[i];
}
return res;
}
void solve()
{
cin>>l>>n>>m;
a[n+1]=l;
for(int i=1;i<=n;++i) cin>>a[i];
int L=0,R=l+1;
while(L+1<R)
{
int M=L+(R-L)/2;
if(calc(M)<=m) L=M;
else R=M;
}
cout<<L<<'\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
[TJOI2007] 路标设置
题目背景
B 市和 T 市之间有一条长长的高速公路,这条公路的某些地方设有路标,但是大家都感觉路标设得太少了,相邻两个路标之间往往隔着相当长的一段距离。为了便于研究这个问题,我们把公路上相邻路标的最大距离定义为该公路的“空旷指数”。
题目描述
现在政府决定在公路上增设一些路标,使得公路的“空旷指数”最小。他们请求你设计一个程序计算能达到的最小值是多少。请注意,公路的起点和终点保证已设有路标,公路的长度为整数,并且原有路标和新设路标都必须距起点整数个单位距离。
输入格式
第 \(1\) 行包括三个数 \(L,N,K\),分别表示公路的长度,原有路标的数量,以及最多可增设的路标数量。
第 \(2\) 行包括递增排列的 \(N\) 个整数,分别表示原有的 \(N\) 个路标的位置。路标的位置用距起点的距离表示,且一定位于区间 \([0,L]\) 内。
输出格式
输出 \(1\) 行,包含一个整数,表示增设路标后能达到的最小“空旷指数”值。
样例 #1
样例输入 #1
101 2 1
0 101
样例输出 #1
51
提示
公路原来只在起点和终点处有两个路标,现在允许新增一个路标,应该把新路标设在距起点 \(50\) 或 \(51\) 个单位距离处,这样能达到最小的空旷指数 \(51\)。
\(50\%\) 的数据中,\(2 \leq N \leq 100\),\(0 \leq K \leq 100\)。
\(100\%\) 的数据中,\(2 \leq N \leq 100000\), \(0 \leq K \leq100000\)。
\(100\%\) 的数据中,\(0 < L \leq 10000000\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10,suf=0x3f3f3f3f;
int a[N];
int l,n,k;
//用calc(x)表示以x为空旷指数需要添加的路标,则函数calc是单调递减的
//那么一定存在一个分界点使得界限左边的都大于k界限右边的都小于等于k
int calc(int x)
{
int res=0;
for(int i=1;i<=n+1;++i)
{
if(a[i]-a[i-1]>x)
{
res+=(a[i]-a[i-1])/x;
res-=((a[i]-a[i-1])%x==0);
}
}
return res;
}
void solve()
{
cin>>l>>n>>k;
a[n+1]=l;
for(int i=1;i<=n;++i) cin>>a[i];
int L=0,R=l+1;
while(L+1<R)
{
int M=L+(R-L)/2;
if(calc(M)>k) L=M;
else R=M;
}
cout<<R<<'\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
数列分段 Section II
题目描述
对于给定的一个长度为N的正整数数列 \(A_{1\sim N}\),现要将其分成 \(M\)(\(M\leq N\))段,并要求每段连续,且每段和的最大值最小。
关于最大值最小:
例如一数列 \(4\ 2\ 4\ 5\ 1\) 要分成 \(3\) 段。
将其如下分段:
第一段和为 \(6\),第 \(2\) 段和为 \(9\),第 \(3\) 段和为 \(1\),和最大值为 \(9\)。
将其如下分段:
第一段和为 \(4\),第 \(2\) 段和为 \(6\),第 \(3\) 段和为 \(6\),和最大值为 \(6\)。
并且无论如何分段,最大值不会小于 \(6\)。
所以可以得到要将数列 \(4\ 2\ 4\ 5\ 1\) 要分成 \(3\) 段,每段和的最大值最小为 \(6\)。
输入格式
第 \(1\) 行包含两个正整数 \(N,M\)。
第 \(2\) 行包含 \(N\) 个空格隔开的非负整数 \(A_i\),含义如题目所述。
输出格式
一个正整数,即每段和最大值最小为多少。
样例 #1
样例输入 #1
5 3
4 2 4 5 1
样例输出 #1
6
提示
对于 \(20\%\) 的数据,\(N\leq 10\)。
对于 \(40\%\) 的数据,\(N\leq 1000\)。
对于 \(100\%\) 的数据,\(1\leq N\leq 10^5\),\(M\leq N\),\(A_i < 10^8\), 答案不超过 \(10^9\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int a[N];
int n,m;
//用calc(x)表示以x为最大区间和分出多少段,则函数calc是单调递减的
//那么一定存在一个分界点使得界限左边的都大于M界限右边的都小于等于M,ans=L
int calc(int x)
{
ll res=0,tmp=0;
for(int i=1;i<=n;++i)
{
if(a[i]>x) return 1e9;
tmp+=a[i];
if(tmp>x) {tmp=a[i];res++;}
}
if(tmp) res++;
return res;
}
void solve()
{
cin>>n>>m;
ll sum=0;
int minn=1e9;
for(int i=1;i<=n;++i)
{
cin>>a[i];sum+=a[i];
minn=min(minn,a[i]);
}
int L=minn-1,R=sum+1;
while(L+1<R)
{
int M=L+(R-L)/2;
if(calc(M)>m) L=M;
else R=M;
}
cout<<R<<'\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
银行贷款
题目描述
当一个人从银行贷款后,在一段时间内他(她)将不得不每月偿还固定的分期付款。这个问题要求计算出贷款者向银行支付的利率。假设利率按月累计。
输入格式
三个用空格隔开的正整数。
第一个整数表示贷款的原值 \(w_0\),第二个整数表示每月支付的分期付款金额 \(w\),第三个整数表示分期付款还清贷款所需的总月数 \(m\)。
输出格式
一个实数,表示该贷款的月利率(用百分数表示),四舍五入精确到 \(0.1\%\)。
样例 #1
样例输入 #1
1000 100 12
样例输出 #1
2.9
提示
数据保证,\(1 \leq w_0, w\leq 2^{31}-1\),\(1 \leq m\leq 3000\)。
点击查看代码
posted on
浙公网安备 33010602011771号