前言
事实证明体力与饮食的重要性,这周被外卖干的快吐了。下周开始锻炼吧。(狗头保命)
A-海底高铁
Description
该铁路经过 N 个城市,每个城市都有一个站。不过,由于各个城市之间不能协调好,于是乘车每经过两个相邻的城市之间(方向不限),必须单独购买这一小段的车票。第 i 段铁路连接了城市 i 和城市 i+1(1≤i<N)。如果搭乘的比较远,需要购买多张车票。第 i 段铁路购买纸质单程票需要 Ai 博艾元。
虽然一些事情没有协调好,各段铁路公司也为了方便乘客,推出了 IC 卡。对于第 i 段铁路,需要花 Ci 博艾元的工本费购买一张 IC 卡,然后乘坐这段铁路一次就只要扣Bi(Bi<Ai) 元。IC 卡可以提前购买,有钱就可以从网上买得到,而不需要亲自去对应的城市购买。工本费不能退,也不能购买车票。每张卡都可以充值任意数额。对于第 i 段铁路的 IC 卡,无法乘坐别的铁路的车。
Uim 现在需要出差,要去 M 个城市,从城市 P1 出发分别按照P1,P2,P3,⋯,PM 的顺序访问各个城市,可能会多次访问一个城市,且相邻访问的城市位置不一定相邻,而且不会是同一个城市。
现在他希望知道,出差结束后,至少会花掉多少的钱,包括购买纸质车票、买卡和充值的总费用。
Input
第一行两个整数,N,M。
接下来一行,M 个数字,表示 Pi。
接下来N−1 行,表示第 i 段铁路的 Ai,Bi,Ci。
Output
一个整数,表示最少花费
Sample 1
Inputcopy | Outputcopy |
---|---|
9 10 3 1 4 1 5 9 2 6 5 3 200 100 50 300 299 100 500 200 500 345 234 123 100 50 100 600 100 1 450 400 80 2 1 10 | 6394 |
Hint
22 到 33 以及 88 到 99 买票,其余买卡。
对于 30%30% 数据 M=2。
对于另外 30%30% 数据 N≤1000,M≤1000。
对于 100%100% 的数据 M,N≤10^5,Ai,Bi,Ci≤10^5。
详解
#include<bits/stdc++.h>
using namespace std;
int v[100005],s[100005];
long long a[100005],b[100005],c[100005];
int main()
{
int n,m,i;
cin>>n>>m;
for(i=1;i<=m;i++){
cin>>v[i];
}
for(i=1;i<n;i++){
cin>>a[i]>>b[i]>>c[i];
}
for(i=1;i<m;i++){
s[min(v[i],v[i+1])]++;
s[max(v[i],v[i+1])]--;
}
for(i=1;i<n;i++){
s[i]+=s[i-1];
}
long long sum=0;
for(i=1;i<n;i++){
sum+=min(a[i]*s[i],b[i]*s[i]+c[i]);
}
cout<<sum;
}
技巧
1、按大小(前后)顺序前缀和;
PS :关注某部分多次重复加减;
B-学生分组
Description
有 n 组学生,给出初始时每组中的学生个数,再给出每组学生人数的上界 R 和下界 L (L≤R),每次你可以在某组中选出一个学生把他安排到另外一组中,问最少要多少次才可以使 N 组学生的人数都在 [L,R] 中。
Input
第一行一个整数 n,表示学生组数;
第二行 n 个整数,表示每组的学生个数;
第三行两个整数 L,R,表示下界和上界。
Output
一个数,表示最少的交换次数,如果不能满足题目条件输出 −1。
Sample 1
Inputcopy | Outputcopy |
---|---|
2 10 20 10 15 | 5 |
Hint
数据范围及约定
对于全部数据,保证 1≤n≤50。
详解
#include<bits/stdc++.h>
using namespace std;
int a[55];
int main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int n,i;
double sum=0;
cin>>n;
for(i=1;i<=n;i++){
cin>>a[i];sum+=a[i];
}
int L,R;
cin>>L>>R;
if(sum/n*1.0<L||1.0*sum/n>R){
cout<<"-1"; return 0;
}
int l=0,r=0;
for(i=1;i<=n;i++){
if(a[i]<L){
l+=(L-a[i]);
}
else if(a[i]>R){
r+=a[i]-R;
}
}
if(l>r) r+=l-r;
cout<<r;
}
技巧
1. 分别算出左移右移次数,输出多的;
C-最大正方形
Description
在一个 n×m 的只包含 0 和 1 的矩阵里找出一个不包含 0 的最大正方形,输出边长。
Input
输入文件第一行为两个整数 n,m(1≤n,m≤100),接下来 n 行,每行 m 个数字,用空格隔开,0 或 1。
Output
一个整数,最大正方形的边长。
Sample 1
Inputcopy | Outputcopy |
---|---|
4 4 0 1 1 1 1 1 1 0 0 1 1 0 1 1 0 1 | 2 |
详解
#include<bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
long long n,m,i,j,a[105][105],s[105][105];
cin>>n>>m;
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
cin>>a[i][j];
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
}
}
int size=min(n,m),ans=0;
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
for(int l=1;l<=size;l++){
int x=i+l-1,y=j+l-1;
if(x>n||y>m||s[x][y]-s[x][j-1]-s[i-1][y]+s[i-1][j-1]!=l*l)
break;
if(ans<l)ans=l;
}
}
}
cout<<ans;
}
D-K倍区间
Description
给定一个长度为 N 的数列,A1,A2,⋯AN,如果其中一段连续的子序列Ai,Ai+1,⋯Aj(i≤j) 之和是 K 的倍数,我们就称这个区间[i,j] 是 K 倍区间。
你能求出数列中总共有多少个 K 倍区间吗?
Input
第一行包含两个整数 N 和MATH:0(1≤Ai≤10^5)。
Output
输出一个整数,代表 K 倍区间的数目。
Sample 1
Inputcopy | Outputcopy |
---|---|
5 2 1 2 3 4 5 | 6 |
Hint
时限 2 秒, 256M。蓝桥杯 2017 年第八届
详解
#include<bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
long long n,k,i,j,a[100005],s[100005];
cin>>n>>k;
s[0]=0;
for(i=1;i<=n;i++){
cin>>a[i];
s[i]=s[i-1]+a[i];
}
long long sum=0;
map<long long,long long> m;
for(i=1;i<=n;i++){
sum+=m[s[i]%k];
m[s[i]%k]++;
}
cout<<sum+m[0];
}
E-一元三次方程求解
Description
有形如:a*x^3+b*x^2+c*x+d=0 这样的一个一元三次方程。给出该方程中各项的系数(a,b,c,d 均为实数),并约定该方程存在三个不同实根(根的范围在 −100 至 100 之间),且根与根之差的绝对值 ≥1≥1。要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后 22 位。
提示:记方程f(x)=0,若存在 22 个数 x1 和 x2,且x1<x2,f(x1)×f(x2)<0,则在 (x1,x2) 之间一定有一个根。
Input
一行,4个实数 a,b,c,d。
Output
一行,33 个实根,从小到大输出,并精确到小数点后 22 位。
Sample 1
Inputcopy | Outputcopy |
---|---|
1 -5 -4 20 | -2.00 2.00 5.00 |
Hint
【题目来源】
NOIP 2001 提高组第一题
详解
#include<bits/stdc++.h>
using namespace std;
double a,b,c,d,i;
double f( double i){
return (a*i*i*i+b*i*i+c*i+d);
}
int main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
cin>>a>>b>>c>>d;
int count=0;
for(i=-100;i<=100;i++){
double l=i,r=i+1;
double x1=f(l);
double x2=f(r);
if(!x1){
printf("%.2lf ",l);
count++;
}
if(x1*x2<0){
double mid;
while(r-l>=0.001){
mid=(l+r)/2;
if(f(r)*f(mid)<=0) l=mid;
else r=mid;
}
printf("%.2lf ",r);
count++;
}
if(count==3) break;
}
}
F-银行贷款
Description
当一个人从银行贷款后,在一段时间内他(她)将不得不每月偿还固定的分期付款。这个问题要求计算出贷款者向银行支付的利率。假设利率按月累计。
Input
三个用空格隔开的正整数。
第一个整数表示贷款的原值 w0,第二个整数表示每月支付的分期付款金额 w,第三个整数表示分期付款还清贷款所需的总月数 m。
Output
一个实数,表示该贷款的月利率(用百分数表示),四舍五入精确到0.1%。
数据保证答案不超过300.0%。
Sample 1
Inputcopy | Outputcopy |
---|---|
1000 100 12 | 2.9 |
Hint
数据保证,1≤w0,w≤2^31−1,1≤m≤3000。
详解
#include<bits/stdc++.h>
using namespace std;
int n,m,k;
bool f( double a){
double sum=n;
for(int i=1;i<=k;i++){
sum+=sum*a;
sum-=m;
}
if(sum<=0) return true;
else return false;
}
int main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
cin>>n>>m>>k;
double l=0,r=500;
while(r-l>0.01){
double mid=(r+l)/2;
if(f(mid/100)) l=mid;
else r=mid;
}
printf("%.1lf",l);
}