普及C组模拟赛2
1.【10.5NOIP普及模拟】sort
小x和小y是好朋友。小y的成绩很差,以至于 GPA(平均绩点)在系内倒数。系内一共有 N 位同学,每位同学有自己的 GPA,以及已修学分数,定义 GPT = GPA ×已修学分数。小x为了帮助小y提高成绩,给小y提了一个要求:新学期的 GPA 要超过系内排名第 K 位的同学。 为了帮助理解,给出一个例子:
现在给出系里面每位同学的 GPT(只有一位小数),以及他们的已修学分。你需要帮助小y把排名 第 K 位的同学的 GPA 求出来。
输入
第 1 行2 个整数 N, K。
第 2- (N + 1) 行,每行 1 个非负实数和 1 个整数,分别表示 GPT 和已 修学分数。
注意:所有同学的学分都在 [1, 250] 的范围。
输出
第 1 行1 个实数,表示排名第 K 同学的 GPA,保留 2 位小数输出。
样例输入
5 3
73 20
79.8 21
72.6 22
85.1 23
65.7 18
样例输出
3.65
数据范围限制
对于 50% 的数据:1 ≤ N ≤ 100。
对于 100% 的数据:1 ≤ K ≤ N ≤ 100000,GPT 小数点后至多 1 位,GPA 至多 4.0。
分析
快排一波
AC代码
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<cstdio>
using namespace std;
int n,k,b;
struct stu//结构体
{
double GPT,GPA;
}a[100005];
bool cmp(stu x,stu y)//快排
{
if(x.GPA==y.GPA)return x.GPT>y.GPT;
return x.GPA>y.GPA;
}
int main()
{
freopen("sort.in","r",stdin);
freopen("sort.out","w",stdout);
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i].GPT>>b;
a[i].GPA=a[i].GPT/b;//公式转化一下
}
sort(a+1,a+n+1,cmp);//快排
cout<<fixed<<setprecision(2)<<a[k].GPA;
fclose(stdin);
fclose(stdout);
}
2.【10.5NOIP普及模拟】sum
题目描述
小x有很多糖果,分成了 N 堆,排成一列。小x说,如果小y能迅速求出第 L 堆到第 R 堆一 共有多少糖果,就把这些糖果都给他。
现在给出每堆糖果的数量,以及每次询问的 L 和 R,你需要帮助小y,把每次询问的结果求出来。 注意,你不需要考虑糖果被小y取走的情况。
输入
第 1 行2 个整数 N, M, 分别表示堆数和询问数量。
第 2 行N 个整数 Ai,表示第 i 堆糖果的数 量。
第 3- (M + 2) 行,每行 2 个整数 Li, Ri,表示第 i 个询问是 [Li, Ri]。
输出
M 行,对于每个询问,输出对应的和。
样例输入
5 5
1 2 3 4 5
1 5
2 4
3 3
1 3
3 5
样例输出
15
9
3
6
12
数据范围限制
对于 50% 的数据:1 ≤ N, M ≤ 100。
对于 100% 的数据:1 ≤ N,M ≤ 100000,0 ≤ Ai ≤ 10000,1 ≤ Li ≤ Ri ≤ N。
正解
前缀和
AC代码
#include<iostream>
#include<cstdio>
using namespace std;
long long n,m,b,l,r,a[100005];
int main()
{
freopen("sum.in","r",stdin);
freopen("sum.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&b);
a[i]=a[i-1]+b;//前缀和
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&l,&r);
printf("%d\n",a[r]-a[l-1]);//记住是l-1,不是l
}
fclose(stdin);
fclose(stdout);
}
3.【10.5NOIP普及模拟】count
题目描述
小x开发了一个奇怪的游戏,这个游戏的是这样的:一个长方形,被分成 N 行 M 列的格子,第 i 行第 j 列的格子记为 (i, j),就是说,左上角的格子是 (1, 1),右下角的格子是 (N, M)。开始的时候,小y在 (1, 1),他需要走到 (N, M)。每一步,小y可以走到正右方或者正下方的一个格子。具体地说,如小y现在在 (x, y),那么他可以走到 (x, y + 1) 或 (x + 1, y)。当然,小y不能走出离开这个长方形。
每个格子有积分,用一个 1~10 的整数表示。经过这个格子,就会获取这个格子的积分(起点和终 点的积分也计算)。通过的方法是:到达 (N, M) 的时候,积分恰好为 P 。
现在给出这个长方形每个格子的积分,你需要帮助小y,求出从起点走到终点,积分为 P 的线路有多少条。
输入
第 1 行3 个整数 N, M, P 。
接下来 N 行,每行 M 个整数 Aij ,表示格子 (i, j) 的积分。
输出
1 行1 个整数,表示积分为 P 线路的数量。
因为数值太大,你只需要输出结果除以 (10^9 + 7) 的 余数。
样例输入
3 3 9
2 2 1
2 2 2
1 2 2
样例输出
2
数据范围限制
对于 50% 的数据:1 ≤ N, M ≤ 10。
对于 100% 的数据:1 ≤ N, M ≤ 100,0 ≤ Aij ≤ 10。
正解
DP
AC代码
#include<iostream>
#include<cstdio>
using namespace std;
int n,m,p,a[105][105];
long long f[105][105][1505];
int main()
{
freopen("count.in","r",stdin);
freopen("count.out","w",stdout);
cin>>n>>m>>p;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>a[i][j];//输入
f[1][1][a[1][1]]=1;//初值
for(int i=1;i<=n;i++)//dp
for(int j=1;j<=m;j++)
for(int k=1;k<=p;k++)
if(k-a[i][j]>0)
f[i][j][k]=(f[i-1][j][k-a[i][j]]+f[i][j-1][k-a[i][j]])%1000000007;//公式
cout<<f[n][m][p];
fclose(stdin);
fclose(stdout);
}
4.【10.5NOIP普及模拟】ranking
题目描述
小x有n个小姊妹(根据典故,我们假设n≤3000)。他每天都喜欢按不同标准给小姊妹们排(da)序(fen)。今天,他突然对小姊妹们的名字产生了兴趣。他觉得小姊妹的魅力和她们的名字有密切联系,于是他觉得所有有相似的名字的小姊妹必须排在一起。
相似是指,名字的开头一个或若干个连续字母相同。
于是,小x定下了如下规则:
在任何以同样的字母序列开头的名字之间,所有名字开头必须是同样的字母序列。
比如,像MARTHA和MARY这两个名字,它们都以MAR开头,所以像MARCO或MARVIN这样的名字可以插入这两个名字中间,而像MAY这样的就不行。
显然,按字典序排序是一个合法的排序方案,但它不是唯一的方案。你的任务就是计算出所有合法的方案数。考虑到答案可能很大,输出答案 mod 1 000 000 007。
输入
第一行一个整数n,小x的小姊妹个数。
第2~n+1行,每行一个字符串,代表这个小姊妹的名字。
输出
一行一个整数,合法的方案数。
样例输入
3
IVO
JASNA
JOSIPA
样例输出
4
数据范围限制
对于60%的数据:3 ≤ n ≤ 10。
对于100%的数据:
3 ≤ n ≤ 3000
1 ≤ 字符串长度 ≤ 3000,并且只含有大写字母。
正解
字符串+搜索
AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int M=1000000007;
long long n,maxn,s[4000]={0,1};
struct stu
{
char aa[4000];
}a[4000];
bool cmp(stu x,stu y)//快排
{
return(strcmp(x.aa,y.aa)<0);
}
long long dfs(int x,int y,int z)//搜索
{
long long ans=1,o=x,t=1;
if(z>maxn)return s[y-x+1];
for(int i=x+1;i<=y;i++)
if(a[i].aa[z]!=a[o].aa[z])
{
t++;
ans=ans*dfs(o,i-1,z+1)%M;
o=i;
}
if(o!=y)ans=ans*dfs(o,y,z+1)%M;
ans=ans*s[t]%M;
}
int main(){
freopen("ranking.in","r",stdin);
freopen("ranking.out","w",stdout);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].aa;//string
if(strlen(a[i].aa)-1>maxn)maxn=strlen(a[i].aa)-1;
}
for(int i=1;i<=n;i++)
for(int j=strlen(a[i].aa);j<=maxn;j++)
a[i].aa[j]='*';
for(int i=2;i<=3000;i++)
s[i]=s[i-1]*i%M;
sort(a+1,a+n+1,cmp);//快排
cout<<dfs(1,n,0);
}
赛后分析
1.
直接AC了
2.
前缀和出了问题,最后AC
3.
50分暴力,后来通过视频AC
4.
cxy(陈博)巨佬告诉我思路,然后AC