华中师范大学蓝桥杯第十一届校内模拟赛
2020/3/22 8:00-12:00
题目还是比省赛要简单的,我9:25就做完了。
第一题
分析:
简单题,求给定的1200000
的正约数的个数。我们可以暴力判因数,也可以对x分解质因数。x = p1^t1 * p2^ t2 * ... * pk^tk
,则约数个数为(t1+1) * (t2+1) * ... * (tk+1)
。
暴力代码:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
void f(int x) {
int ans = 0;
for(int i=1;i<=x;++i)
if(x%i==0) ++ans;
cout<<ans<<endl;
}
int main(void) {
f(1200000);
return 0;
}
分解质因数代码:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
void getAns(int x) {
long long res = 1;
for(int i=2;i<=x/i;++i) {
if(x%i) continue;
int c = 0;
while(x%i==0) ++c,x/=i;
res = res * (c+1);
}
if(x>1) res *= 2;
cout<<res<<endl;
}
int main(void) {
getAns(1200000);
return 0;
}
答案为:96
第二题:
答案为:15.125GB = 15.125*1024MB = 15488
MB
第三题:
分析:
我们通过分析或寻找规律,可以得到n个节点的二叉树叶子节点个数最多为floor((n+1)/2)
答案:(2019+1) / 2 = 1010
分析:
从1到2019循环一遍,逐个判断即可。
代码:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
int main(void) {
int ans = 0;
for(int i=1;i<=2019;++i) {
int x = i;
int ok = 0;
while(x) {
if(x%10==9) ok=1;
x/=10;
}
ans += ok;
}
cout<<ans<<endl;
return 0;
}
答案:544
第五题:
分析:
题意很简单,就是问有多少个数,钱面有比它小的,后面有比它大的。
只要前面的最小值小于x,后面的最大值大于x即可。我们可以维护前缀最大值和后缀最大值。
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
int a[1001],Max[1001],Min[1001];
int main(void) {
int n;cin>>n;
for(int i=1;i<=n;++i) scanf("%d",a+i);
Min[1] = a[1];
for(int i=2;i<=n;++i) Min[i] = min(Min[i-1],a[i]);
Max[n] = a[n];
for(int i=n-1;i>=1;--i) Max[i] = max(Max[i+1],a[i]);
int res = 0;
for(int i=2;i<n;++i) {
if(Min[i-1]<a[i]&&Max[i+1]>a[i]) ++res;
}
cout<<res<<endl;
return 0;
}
第六题:
分析:
dfs即可。暴力判断应该也可以,但是1E6的nlogn常数稍大。
代码:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <vector>
using namespace std;
int n,ans;
void dfs(int v,int pre) {
if(pre*10+v>n) return;
++ans;
pre = pre*10+v;
for(int i=v;i<10;++i)
dfs(i,pre);
}
int main(void) {
cin>>n;
ans = 0;
for(int i=1;i<=9;++i)
dfs(i,0);
cout<<ans<<endl;
return 0;
}
第七题:
分析:
按题意模拟即可。
代码:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
int a[1000];
char s[1000];
bool isY(char c) {
return (c=='a'||c=='e'||c=='i'||c=='o'||c=='u');
}
int main(void) {
cin>>(s+1);
int len = strlen(s+1);
for(int i=1;i<=len;++i) {
if(isY(s[i])) a[i] = 1;
else a[i] = 0;
}
int p = 1;
if(a[1]!=0) {
puts("no");return 0;
}
while(p<len&&a[++p]==0);
if(a[p]!=1) {
puts("no");return 0;
}
while(p<len&&a[++p]==1);
if(a[p]!=0) {
puts("no");return 0;
}
while(p<len&&a[++p]==0);
if(a[p]!=1) {
puts("no");return 0;
}
puts("yes");
return 0;
}
第八题:
样例输入:
4 5
.g...
.....
..g..
.....
2
样例输出
gggg.
gggg.
ggggg
.ggg.
分析:
按照题意模拟即可。n,m,k范围都是1000,模拟k天可能会被卡。我们可以做两个小优化如果根本没有g,那么以后也没有g,直接输出。如果已经长满草了,那么剩下的天数就不用再模拟了。
代码:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <vector>
using namespace std;
const int N = 1001;
char a[N][N];
bool vis[N][N];
int n,m,k;
int dx[] = {1,-1,0,0};
int dy[] = {0,0,1,-1};
bool ok(int x,int y) {
return (x>=0&&x<n&&y>=0&&y<m);
}
vector<pair<int,int> >v;
int main(void) {
scanf("%d%d",&n,&m);
int sz = 0;
for(int i=0;i<n;++i) scanf("%s",a[i]);
scanf("%d",&k);
for(int i=0;i<n;++i)
for(int j=0;j<m;++j)
if(a[i][j]=='g') ++sz,v.push_back(make_pair(i,j)),vis[i][j]=true;
if(sz==0) { //根本就没有草
for(int i=0;i<n;++i) puts(a[i]);
return 0;
}
while(k--) {
int add = 0;
for(int i=0;i<sz;++i) {
for(int j=0;j<4;++j) {
int x = v[i].first+dx[j];
int y = v[i].second+dy[j];
if(ok(x,y)&&!vis[x][y])
vis[x][y]=true,v.push_back(make_pair(x,y)),a[x][y]='g',++add;
}
}
sz += add;
if(sz==n*m) break; //已经长满了
}
for(int i=0;i<n;++i) puts(a[i]);
return 0;
}
第九题:
分析:
每个序列是否能再拓展只和倒数两个数有关。我们可以借此定义二维状态。设dp[x][y]代表最后一个为y,倒数第二个数为x的数列能拓展出的数列个数。定义好状态我们就可以记忆化搜索了。
代码:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
const int N = 1001;
const int mod = 10000;
int dp[N][N];
int dfs(int p1,int p) {
if(dp[p1][p]) return dp[p1][p];
int ans = 1, dif = abs(p1-p);
for(int i=1;i<dif;++i)
ans = (ans + dfs(p,i))%mod;
return dp[p1][p] = ans;
}
using namespace std;
int main(void) {
int n;
while(cin>>n) {
int res = 0;
for(int i=1;i<=n;++i)
res = (res+dfs(n,i))%mod;
cout<<res<<endl;
}
return 0;
}
第十题:
样例输入:
5 3
3 1 2 5 4
样例输出:
3 5 4
自测的样例输入:
12 3
1 1 7 7 1 2 3 6 6 6 8 4
自测的样例输出:
7 8 4
分析:
由题意值,我们要求的是字典序最大的长度为m的子序列。字典序最大,我们肯定要贪心。我们再取面的数的时候,还需要考虑后面还有没有节目可以选满m个。所以,我们可以每次从[pos[i-1]+1,n-m+i]
这个区间中找一个最靠前的最大值作为pos[i]。静态区间最大值我们可以用RMQ查询,也可以用线段树或树状数组。
代码:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int N = 100001;
int Max[N][20],a[N],Log2[N]={-1},r[N],n;
int query(int x,int y) {
if(x>y) swap(x,y);
int j = Log2[y-x+1];
int pa = Max[x][j], pb = Max[y-(1<<j)+1][j];
return a[pa]<a[pb]?pb:pa;
}
int main(void) {
int m,n;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
scanf("%d",a+i);
for(int i=1;i<=n;++i) Max[i][0] = i,Log2[i]=Log2[i>>1]+1;
for(int j=1;(1<<j)<n;++j) {
for(int i=1;(1<<j)+i-1<=n;++i) {
int pa = Max[i][j-1], pb = Max[i+(1<<(j-1))][j-1];
Max[i][j] = a[pa]<a[pb]?pb:pa;
}
}
r[1] = query(1,n-m+1);
for(int i=2;i<=m;++i)
r[i] = query(r[i-1]+1,n-m+i);
for(int i=1;i<=m;++i)
printf("%d%c",a[r[i]],i==n?'\n':' ');
return 0;
}
xzc
2020/3/22 11:16