test20190611 NOIP模拟赛
题一:答题比赛
【问题描述】
YYH报名参加了一个特殊的电视问答节目。这个节目共有n个问题,每回答正确1题,YYH就会获得1分,而每当YYH连续答对k题,那么他的现有得分乘以2,注意答对第k题后,是先加1分到总分中,再把总分乘以2,此时连续答对题目计数器会清零。现在YYH成功对了m题,他想知道他的最小得分。因为这个数字可能很大,你只需要输出这个数对1,000,000,009取模的结果即可。
【输入格式】
仅一行,三个数n,m,k如题目描述。
【输出格式】
仅一行,一个数,YYH的最小得分。
【输入输出样例1】
exam.in
5 3 2
exam.out
3
【输入输出样例2】
exam.in
5 4 2
exam.out
6
【样例解释】
样例1答对第1、3、5题可以获得最低分3分;
样例2只答错第4题,可以获得最低分4+2分。
【数据范围】
对于30%的数据,n,m,k<=10
对于60%的数据,n,m,k<=1000
对于100%的数据,2<=k<=n<=10^9,0<=m<=n
这个显然是优先往后放k-1个空一格。然后前面不够了的就连续放就行了。证明这个结论也很简单。
开始没取模,然后又有除0……然而我不想写数列递推或者矩阵乘法了,所以TLE了2个点。
#include<bits/stdc++.h>
#define co const
template<class T>T read(){
T data=0,w=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-') w=-w;
for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
return data*w;
}
template<class T>T read(T&x){
return x=read<T>();
}
typedef long long ll;
using namespace std;
co int mod=1e9+9;
int mul(int x,int y){
return (ll)x*y%mod;
}
int add(int x,int y){
return (x+=y)>=mod?x-mod:x;
}
int main(){
freopen("exam.in","r",stdin),freopen("exam.out","w",stdout);
int n=read<int>(),m=read<int>(),k=read<int>();
int t=k>1?min(n-m,min(n/k,m/(k-1))):0;
m-=t*(k-1);
int ans=0;
for(;m;m-=k){
if(m<k){
ans=add(ans,m);
break;
}
ans=mul(2,add(ans,k));
}
printf("%d\n",add(ans,t*(k-1)));
return 0;
}
题二:人类基因组
【问题描述】
L教授最近正在研究一个关于人类基因的项目,基因可以被看作一个长度为n的序列:A0,A1,......,An-1。对于这个基因序列循环移动k位之后,就可以得到一个新的基因序列为:Ak,Ak+1,…,An-1,A0,A1,…,Ak-1。当一个基因序列满足对于任意的前i(1<=i<=n)项和都满足不小于0,我们就称这个基因序列为优质基因序列。
由于L教授最近工作比较繁忙,所以找到了正在实验室闲逛的你,你的任务就是帮L教授统计出所有优质基因序列的个数。
【输入格式】
第一行一个整数n,表示基因序列的长度。
第二行n个整数,依次为A0,A1,......,An-1的值。
【输出格式】
输出仅一个整数,表示优质基因序列的个数。
【输入输出样例1】
genes.in
3
2 2 3
genes.out
3
【样例1说明】
3个元素的序列:2 2 3,循环移动情况如下:
循环移动0位得到序列:2 2 3,前i项和分别为:2 4 7,符合条件;
循环移动1位得到序列:2 3 2,前i项和分别为:2 5 7;符合条件;
循环移动2位得到序列:3 2 2,前i项和分别为:3 5 7;符合条件;
【输入输出样例2】
genes.in
5
3 -1 2 -3 4
genes.out
2
【数据范围】
对于30%的数据,满足1<=N<=5,000
对于50%的数据,满足1<=N<=10,000
对于100%的数据,满足1<=N<=1,000,000,-1,000<=Ai<=1,000
把前缀和拿出来推一推式子就行了。扫描维护最小值。
开始把大于等于0看成大于0了。
#include<bits/stdc++.h>
#define co const
template<class T>T read(){
T data=0,w=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-') w=-w;
for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
return data*w;
}
template<class T>T read(T&x){
return x=read<T>();
}
typedef long long ll;
using namespace std;
co int N=1e6+1;
int n,s[N];
bool v[N];
int main(){
freopen("genes.in","r",stdin),freopen("genes.out","w",stdout);
read(n);
for(int i=1;i<=n;++i) s[i]=s[i-1]+read<int>();
for(int i=n-1,mv=s[n];i>=0;mv=min(mv,s[i]),--i)
v[i]=mv>=s[i];
int ans=v[0];
for(int i=1,mv=s[1];i<=n-1;++i,mv=min(mv,s[i]))
ans+=v[i]&(mv+s[n]-s[i]>=0);
printf("%d\n",ans);
return 0;
}
题三:最短路径
【问题描述】
平面内给出 n 个点,记横坐标最小的点为 A,最大的点为 B,现在Zxd想要知道在每个点经过一次(A 点两次)的情况下从 A 走到 B,再回到 A 的最短路径。但他是个强迫症患者,他有许多奇奇怪怪的要求与限制条件:
- 从 A 走到 B 时,只能由横坐标小的点走到大的点。
- 由 B 回到 A 时,只能由横坐标大的点走到小的点。
- 有两个特殊点 b1 和 b2, b1 在 0 到 n-1 的路上,b2 在 n-1 到 0 的路上。
请你帮他解决这个问题助他治疗吧!
【输入格式】
第一行三个整数 n,b1,b2,( 0 < b1,b2 < n-1 且 b1 <> b2)。n 表示点数,从 0 到 n-1 编号,b1 和 b2 为两个特殊点的编号。
以下 n 行,每行两个整数 x、y 表示该点的坐标(0 <= x,y <= 2000),从 0 号点顺序给出。Doctor Gao为了方便他的治疗,已经将给出的点按 x 增序排好了。
【输出格式】
仅一行,输出最短路径长度(精确到小数点后面 2 位)。
【样例输入】
5 1 3
1 3
3 4
4 1
7 5
8 3
【样例输出】
18.18
【样例解释】
最短路径:0->1->4->3->2->0
【数据范围】
20%的数据n<=20
60%的数据n<=300
100%的数据n<=1000
对于所有数据x,y,b1,b2如题目描述。
把回来那条路看成去的时候走两条路,然后变成了紫书上面一道题。
由于这题有b1,b2的限制条件,所以状态不能减半。f[i,j]表示两终点情况下的最小值,并且要保证小于等于max(i,j)的点都走过了。转移只能往max(i,j)+1转移。
还是DP比较熟练。
#include<bits/stdc++.h>
#define co const
template<class T>T read(){
T data=0,w=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-') w=-w;
for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
return data*w;
}
template<class T>T read(T&x){
return x=read<T>();
}
typedef long long ll;
using namespace std;
co int N=1e3;
int n,b1,b2;
#define x first
#define y second
pair<int,int> p[N];
double dis(int a,int b){
return sqrt(pow(p[a].x-p[b].x,2)+pow(p[a].y-p[b].y,2));
}
double f[N][N];
int main(){
freopen("paths.in","r",stdin),freopen("paths.out","w",stdout);
read(n),read(b1),read(b2);
for(int i=0;i<n;++i) read(p[i].x),read(p[i].y);
for(int i=0;i<n;++i) fill(f[i],f[i]+n,1e18);
f[0][0]=0;
for(int i=0;i<n-1;++i)for(int j=0;j<n-1;++j)if(f[i][j]<1e18){
int t=max(i,j)+1;
if(t!=b2) f[t][j]=min(f[t][j],f[i][j]+dis(i,t));
if(t!=b1) f[i][t]=min(f[i][t],f[i][j]+dis(j,t));
}
double ans=1e18;
for(int i=0;i<n-1;++i){
ans=min(ans,f[n-1][i]+dis(i,n-1));
ans=min(ans,f[i][n-1]+dis(i,n-1));
}
printf("%.2lf\n",ans);
return 0;
}
165->290