当前位置:   article > 正文

HPU2020蓝桥杯省赛训练(一) 题解_p.;.,z,(r((llolo:(e),b()(d(c@

p.;.,z,(r((llolo:(e),b()(d(c@

目录

A

算法提高 找素数

B

算法训练 区间k大数查询

C

算法训练 操作格子

D

算法提高 士兵排队问题

E

基础练习 十六进制转八进制

F

算法训练 最大最小公倍数

G

算法提高 欧拉函数

H

算法训练 乘法次数

I

算法训练 方格取数


​​​​

资源限制

时间限制:1.0s   内存限制:256.0MB

问题描述

  给定区间[L, R] , 请计算区间中素数的个数。

输入格式

  两个数L和R。

输出格式

  一行,区间中素数的个数。

样例输入

2 11

样例输出

5

数据规模和约定

  2 <= L <= R <= 2147483647 R-L <= 1000000

思路:数据范围过大.普通的素数打表肯定是不行的,注意使用R-L <= 1000000这个条件,对sqrt(R)以内的素数进行打表,筛出L到R中素数的倍数(即合数),保存和数-L的状态,遍历得到结果.

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<algorithm>
  6. typedef long long ll;
  7. using namespace std;
  8. const int N = 1000000 + 10;
  9. int prime[N];
  10. int num[N];
  11. int ans[N];
  12. int cnt=0;
  13. void prim(int n)
  14. {
  15. for(int i=2; i<=n; i++)
  16. {
  17. if(!prime[i])
  18. {
  19. num[cnt++] = i;
  20. for(int j=i*2; j<=n; j+=i)
  21. {
  22. prime[j] = 1;
  23. }
  24. }
  25. }
  26. }
  27. int main()
  28. {
  29. int l,r;
  30. cin>>l>>r;
  31. prim((int)sqrt(r));
  32. for(int i=0; i<cnt; i++)
  33. {
  34. int a = num[i];
  35. for(ll j=(l+a-1)/a*a; j<=r; j+=a)//注意减1,否则容易漏掉l-l+a中a的倍数.
  36. {
  37. if(j == a) continue;
  38. else ans[j-l] = 1;
  39. }
  40. }
  41. int cnt = 0;
  42. for(int i=0; i<=r-l; i++)
  43. {
  44. if(ans[i] == 0) cnt++;
  45. }
  46. cout<<cnt;
  47. return 0;
  48. }

时间限制:1.0s   内存限制:256.0MB

问题描述

给定一个序列,每次询问序列中第l个数到第r个数中第K大的数是哪个。

输入格式

第一行包含一个数n,表示序列长度。

第二行包含n个正整数,表示给定的序列。

第三个包含一个正整数m,表示询问个数。

接下来m行,每行三个数l,r,K,表示询问序列从左往右第l个数到第r个数中,从大往小第K大的数是哪个。序列元素从1开始标号。

输出格式

总共输出m行,每行一个数,表示询问的答案。

样例输入

5
1 2 3 4 5
2
1 5 2
2 3 2

样例输出

4
2

数据规模与约定

对于30%的数据,n,m<=100;

对于100%的数据,n,m<=1000;

保证k<=(r-l+1),序列中的数<=106。

 思路:把原数组L-R复制到处理数组中,从大到小进行排序,输出第K个.

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<algorithm>
  6. typedef long long ll;
  7. using namespace std;
  8. const int N = 1000000 + 10;
  9. int ans;
  10. int num[N];
  11. int temp[N];
  12. bool cmp(int a, int b)
  13. {
  14. return a > b;
  15. }
  16. int main()
  17. {
  18. int n,m;
  19. cin>>n;
  20. for(int i=1; i<=n; i++)
  21. scanf("%d",&num[i]);
  22. cin>>m;
  23. int l,r,k;
  24. while(m--)
  25. {
  26. scanf("%d %d %d",&l,&r,&k);
  27. int cnt=1;
  28. for(int i=l; i<=r; i++)
  29. temp[cnt++] = num[i];
  30. sort(temp + 1, temp + cnt, cmp);
  31. printf("%d\n",temp[k]);
  32. }
  33. return 0;
  34. }

 

资源限制

时间限制:1.0s   内存限制:256.0MB

问题描述

有n个格子,从左到右放成一排,编号为1-n。

共有m次操作,有3种操作类型:

1.修改一个格子的权值,

2.求连续一段格子权值和,

3.求连续一段格子的最大值。

对于每个2、3操作输出你所求出的结果。

输入格式

第一行2个整数n,m。

接下来一行n个整数表示n个格子的初始权值。

接下来m行,每行3个整数p,x,y,p表示操作类型,p=1时表示修改格子x的权值为y,p=2时表示求区间[x,y]内格子权值和,p=3时表示求区间[x,y]内格子最大的权值。

输出格式

有若干行,行数等于p=2或3的操作总数。

每行1个整数,对应了每个p=2或3操作的结果。

样例输入

4 3
1 2 3 4
2 1 3
1 4 3
3 1 4

样例输出

6
3

数据规模与约定

对于20%的数据n <= 100,m <= 200。

对于50%的数据n <= 5000,m <= 5000。

对于100%的数据1 <= n <= 100000,m <= 100000,0 <= 格子权值 <= 10000。

思路:线段树模板题,需要同时维护一个maxn值和一个sum值.

  1. #include<iostream>
  2. #include<climits>
  3. #define ll long long
  4. using namespace std;
  5. const int N = 1e5 + 10;
  6. int n,m;
  7. struct node{
  8. int l,r;
  9. int maxn;
  10. int sum;
  11. }tree[4*N];
  12. int num[N];
  13. void build(int u,int l,int r)
  14. {
  15. if(l == r)
  16. {
  17. tree[u] = {l,r,num[r],num[r]};
  18. }
  19. else
  20. {
  21. int mid = (r + l) >> 1;
  22. build(u << 1, l, mid);
  23. build(u << 1 | 1, mid + 1, r);
  24. tree[u] = {l,r};
  25. tree[u].maxn = max(tree[u << 1].maxn, tree[u << 1 | 1].maxn);
  26. tree[u].sum = tree[u << 1].sum + tree[u << 1 | 1].sum;
  27. }
  28. }
  29. int query1(int u, int l ,int r)
  30. {
  31. if(tree[u].l >= l && tree[u].r <= r) return tree[u].maxn;
  32. int maxnn = INT_MIN;
  33. int mid = (tree[u].l + tree[u].r) >> 1;
  34. if(mid >= l) maxnn = query1(u << 1, l, r);
  35. if(mid < r) maxnn = max(maxnn, query1(u << 1 | 1, l, r));
  36. return maxnn;
  37. }
  38. int query2(int u, int l ,int r)
  39. {
  40. if(tree[u].l >= l && tree[u].r <= r) return tree[u].sum;
  41. int sum = 0;
  42. int mid = (tree[u].l + tree[u].r) >> 1;
  43. if(mid >= l) sum = query2(u << 1, l, r);
  44. if(mid < r) sum += query2(u << 1 | 1, l, r);
  45. return sum;
  46. }
  47. void modify(int u, int x, int v)
  48. {
  49. if(tree[u].l == tree[u].r)
  50. {
  51. tree[u].maxn = v;
  52. tree[u].sum = v;
  53. }
  54. else
  55. {
  56. int mid =(tree[u].l + tree[u].r) >> 1;
  57. if(x <= mid) modify(u << 1, x, v);
  58. else modify(u << 1 | 1,x,v);
  59. tree[u].sum = tree[u << 1].sum + tree[u << 1 | 1].sum;
  60. tree[u].maxn = max(tree[u << 1].maxn, tree[u << 1 | 1].maxn);
  61. }
  62. }
  63. int main()
  64. {
  65. cin>>n>>m;
  66. for(int i=1; i<=n; i++)
  67. {
  68. scanf("%d",&num[i]);
  69. }
  70. build(1,1,n);
  71. for(int i=0; i<m; i++)
  72. {
  73. int opt;
  74. cin>>opt;
  75. if(opt == 1)
  76. {
  77. int x, y;
  78. cin>>x>>y;
  79. modify(1,x,y);
  80. }
  81. else if(opt == 2)
  82. {
  83. int l,r;
  84. cin>>l>>r;
  85. printf("%lld\n",query2(1,l,r));
  86. }
  87. else if(opt == 3)
  88. {
  89. int l,r;
  90. cin>>l>>r;
  91. cout<<query1(1,l,r)<<endl;
  92. }
  93. }
  94. return 0;
  95. }

资源限制

时间限制:1.0s   内存限制:256.0MB

试题

  有N个士兵(1≤N≤26),编号依次为A,B,C,…,队列训练时,指挥官要把一些士兵从高到矮一次排成一行,但现在指挥官不能直接获得每个人的身高信息,只能获得“P1比P2高”这样的比较结果(P1、P2∈A,B,C,…,Z,记为 P1>P2),如”A>B”表示A比B高。
  请编一程序,根据所得到的比较结果求出一种符合条件的排队方案。
  (注:比较结果中没有涉及的士兵不参加排队)

输入要求

  比较结果从文本文件中读入(文件由键盘输入),每个比较结果在文本文件中占一行。

输出要求

  若输入数据无解,打印“No Answer!”信息,否则从高到矮一次输出每一个士兵的编号,中间无分割符,并把结果写入文本文件中,文件由键盘输入:

样例输入

A>B
B>D
F>D

样例输出

AFBD

思路:拓扑排序模板题,判断有没有环,无环即可.

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<algorithm>
  6. #include<queue>
  7. #include<vector>
  8. typedef long long ll;
  9. using namespace std;
  10. const int N = 300;
  11. int in[N];
  12. vector <char> v[N];
  13. vector <char> ans;
  14. bool st[N];
  15. int main()
  16. {
  17. freopen("in.txt","r",stdin);
  18. freopen("out.txt","w",stdout);
  19. char c[4];
  20. while(~scanf("%s",c+1))
  21. {
  22. char x = c[1];
  23. char y = c[3];
  24. v[x].push_back(y);
  25. in[y]++;
  26. st[x]=st[y]=1;
  27. }
  28. queue<char> q;
  29. int total = 0;
  30. for(int i='A'; i<='Z'; i++)
  31. {
  32. if(in[i] == 0 && st[i] == 1)
  33. {
  34. q.push(i);
  35. }
  36. if(st[i] == 1) total++;
  37. }
  38. while(!q.empty())
  39. {
  40. char tem = q.front();
  41. total--;
  42. ans.push_back(tem);
  43. q.pop();
  44. for(int i=0; i<v[tem].size(); i++)
  45. {
  46. in[v[tem][i]]--;
  47. if(in[v[tem][i]] == 0) q.push(v[tem][i]);
  48. }
  49. }
  50. if(total == 0)
  51. {
  52. for(int i=0; i<ans.size(); i++)
  53. cout<<ans[i];
  54. }
  55. else puts("No Answer!");
  56. return 0;
  57. }

资源限制

时间限制:1.0s   内存限制:512.0MB

问题描述
  给定n个十六进制正整数,输出它们对应的八进制数。

输入格式
  输入的第一行为一个正整数n (1<=n<=10)。
  接下来n行,每行一个由0~9、大写字母A~F组成的字符串,表示要转换的十六进制正整数,每个十六进制数长度不超过100000。

输出格式
  输出n行,每行为输入对应的八进制正整数。

  【注意
  输入的十六进制数不会有前导0,比如012A。
  输出的八进制数也不能有前导0。

样例输入
  2
  39
  123ABC

样例输出
  71
  4435274

  提示
  先将十六进制数转换成某进制数,再由某进制数转换成八进制。

思路:利用sscanf的16进制读取功能每六位读取一次,再利用printf的8进制的输出功能输出,注意对不足六位的16进制数及转化后不足八位的16进制数进行特殊处理.

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<cstdio>
  5. using namespace std;
  6. const int N = 100000 + 10;
  7. char num[N];
  8. int main()
  9. {
  10. int n;
  11. cin>>n;
  12. for(int i=0; i<n; i++)
  13. {
  14. scanf("%s",num);
  15. int len = strlen(num);
  16. int x;
  17. if(len % 6)//如果16进制数不能刚好分成多个8位8进制数,则在开头对多出来的部分特殊处理
  18. {
  19. switch(len % 6)
  20. {
  21. case 1:
  22. sscanf(num,"%1x",&x);
  23. printf("%o",x);
  24. break;//别忘了break
  25. case 2:
  26. sscanf(num,"%2x",&x);
  27. printf("%o",x);
  28. break;
  29. case 3:
  30. sscanf(num,"%3x",&x);
  31. printf("%o",x);
  32. break;
  33. case 4:
  34. sscanf(num,"%4x",&x);
  35. printf("%o",x);
  36. break;
  37. case 5:
  38. sscanf(num,"%5x",&x);
  39. printf("%o",x);
  40. break;
  41. }
  42. }
  43. int j = len % 6;
  44. while(j < len)
  45. {
  46. sscanf(num+j,"%6x",&x);
  47. if(j==0) printf("%o",x);
  48. else printf("%08o",x); //在16进制数的开头之后可能有6位数太小,转化成8进制数不足8位,需要在不足的位置添0
  49. j+=6;
  50. }
  51. puts("");
  52. }
  53. return 0;
  54. }

 

资源限制

时间限制:1.0s   内存限制:256.0MB

问题描述

已知一个正整数N,问从1~N中任选出三个数,他们的最小公倍数最大可以为多少。

输入格式

输入一个正整数N。

输出格式

输出一个整数,表示你找到的最小公倍数。

样例输入

9

样例输出

504

数据规模与约定

1 <= N <= 106。

思路:这题的关键在与只选取3个数,我们知道如果只选择两个数的话,那么肯定选两个相邻的两数,因为他两互质所以最小公倍数是最大的。所以面对要选择三个数,我们也可以设法选出3个互质的数。对于相邻的a,b,c三个数有奇偶奇,偶奇偶,可以发现奇偶奇三个数一定是互质的,a与b互质,b与c互质,a与b之间不可有共因子2,|c-a|<3,所以也不可能存在共因子3,之后的更别说了。
然后是偶奇偶,其最大最小公倍数必定至少除2,所以可以尝试把其中一个偶换成奇数,且不构成3的倍数,或者整体-1,变成奇偶奇。

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <map>
  4. #include <string>
  5. #include <queue>
  6. #include <vector>
  7. using namespace std;
  8. const int maxn = 300;
  9. typedef pair<int,int> pii;
  10. typedef long long ll;
  11. ll N;
  12. int main(){
  13. cin>>N;
  14. ll mx = 0;
  15. if(N<3) mx = N;
  16. else if(N&1){ //奇偶奇
  17. mx = N*(N-1)*(N-2);
  18. }else{
  19. if(N%3 !=0){ //N和N-3不是3的倍数
  20. mx = N*(N-1)*(N-3);
  21. }else{ //整体-1
  22. mx = (N-1)*(N-2)*(N-3);
  23. }
  24. }
  25. cout<<mx<<endl;
  26. return 0;
  27. }

资源限制

时间限制:1.0s   内存限制:512.0MB

说明

  2016.4.5 已更新试题,请重新提交自己的程序。

问题描述

  给定一个大于1,不超过2000000的正整数n,输出欧拉函数,phi(n)的值。
  如果你并不了解欧拉函数,那么请参阅提示。

输入格式

  在给定的输入文件中进行读入:
  一行一个正整数n。

输出格式

  将输出信息输出到指定的文件中:
  一行一个整数表示phi(n)。

样例输入

17

样例输出

16

提示

  欧拉函数phi(n)是数论中非常重要的一个函数,其表示1到n-1之间,与n互质的数的个数。显然的,我们可以通过定义直接计算phi(n)。
  当然,phi(n)还有这么一种计算方法。
  首先我们对n进行质因数分解,不妨设n=p1^a1 * p2^a2 * ... * pk^ak (这里a^b表示a的b次幂,p1到pk为k个互不相同的质数,a1到ak均为正整数),那么
  phi(n)=n(1-(1/p1))(1-(1/p2))....(1-(1/pk))
  稍稍化简一下就是
  phi(n)=n(p1-1)(p2-1)...(pk-1)/(p1*p2*...*pk)

计算的时候小心中间计算结果超过int类型上界,可通过调整公式各项的计算顺序避免(比如先做除法)!

用到的算法:欧拉筛
线性复杂度,把前maxn个数的欧拉值筛选出来

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <map>
  4. #include <string>
  5. #include <queue>
  6. #include <vector>
  7. using namespace std;
  8. const int maxn = 2e6+10;
  9. typedef pair<int,int> pii;
  10. typedef long long ll;
  11. ll N;
  12. int E[maxn];
  13. void init()
  14. {
  15. E[1] = 1;
  16. for(int i=2;i<maxn;i++){
  17. if(!E[i])
  18. for(int j=i;j<maxn;j+=i){
  19. if(!E[j]) E[j]=j;
  20. E[j] = E[j]/i*(i-1);
  21. }
  22. }
  23. }
  24. int main(){
  25. init();
  26. cin>>N;
  27. cout<<E[N]<<endl;
  28. return 0;
  29. }

 

资源限制

时间限制:1.0s   内存限制:999.4MB

问题描述

  给你一个非零整数,让你求这个数的n次方,每次相乘的结果可以在后面使用,求至少需要多少次乘。如24:2*2=22(第一次乘),22*22=24(第二次乘),所以最少共2次;

输入格式

  第一行m表示有m(1<=m<=100)组测试数据;
  每一组测试数据有一整数n(0<n<=100000000);

输出格式

  输出每组测试数据所需次数s;

样例输入

3
2
3
4

样例输出

1
2
2

用到的算法:快速幂

思路:明显可以看出,求出n次幂的最快方法应该是不断累乘,即2^4 * 2^4,2^8 * 2^8,尽快求出n的二进制最高位的2^poww,再乘以已经算出过的低位即可.

  1. #include <iostream>
  2. #include <cmath>
  3. using namespace std;
  4. typedef long long ll;
  5. const ll mod = 1000000007;
  6. int T;
  7. ll ksm(ll a,ll b){
  8. int poww = 0,cnt = 0;
  9. while(b){
  10. if(b&1) {
  11. cnt += 1;
  12. }
  13. a = a*a% mod;// 也可以不写
  14. poww++;
  15. b>>=1;
  16. }
  17. return poww+cnt-2;
  18. }
  19. int main(){
  20. cin>>T;
  21. while(T--){
  22. ll b;scanf("%lld",&b);
  23. printf("%lld\n",ksm(2,b));
  24. }
  25. return 0;
  26. }

资源限制

时间限制:1.0s   内存限制:256.0MB

问题描述
  设有N*N的方格图(N<=10),我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0。
  某人从图的左上角的A 点(1,1)出发,可以向下行走,也可以向右走,直到到达右下角的B点(N,N)。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。
  此人从A点到B 点共走两次,试找出2条这样的路径,使得取得的数之和为最大。
入格式
  输入的第一行为一个整数N(表示N*N的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的0表示输入结束。
出格式
  只需输出一个整数,表示2条路径上取得的最大的和。

  8
  2 3 13
  2 6 6
  3 5 7
  4 4 14
  5 2 21
  5 6 4
  6 3 15
  7 2 14
  0 0 0
样例输出
  67

思路:四维DP,两个人同时走.

  1. #include<iostream>
  2. #include<map>
  3. #include<cmath>
  4. using namespace std;
  5. const int N = 11;
  6. int val[N][N];
  7. int dp[N][N][N][N];
  8. int main()
  9. {
  10. int n;
  11. cin>>n;
  12. int x,y,z;
  13. while(cin>>x>>y>>z&&x&&y&&z)
  14. {
  15. val[x][y] = z;
  16. }
  17. dp[1][1][1][1] = val[1][1];
  18. for(int x1=1; x1<=n; x1++)
  19. {
  20. for(int y1=1; y1<=n; y1++)
  21. {
  22. for(int x2=1; x2<=n; x2++)
  23. {
  24. for(int y2=1; y2<=n; y2++)
  25. {
  26. if(x1+y1 != x2+y2) continue;
  27. int tem = 0;
  28. tem = max(dp[x1-1][y1][x2-1][y2],dp[x1-1][y1][x2][y2-1]);
  29. tem = max(dp[x1][y1-1][x2-1][y2], tem);
  30. tem = max(dp[x1][y1-1][x2][y2-1], tem);
  31. dp[x1][y1][x2][y2] = tem + val[x1][y1] + val[x2][y2];
  32. if(x1 == x2 && y1 == y2) dp[x1][y1][x2][y2] -=val[x1][y1];
  33. }
  34. }
  35. }
  36. }
  37. cout<<dp[n][n][n][n];
  38. return 0;
  39. }
  40. //8
  41. //2 3 13
  42. //2 6 6
  43. //3 5 7
  44. //4 4 14
  45. //5 2 21
  46. //5 6 4
  47. //6 3 15
  48. //7 2 14
  49. //0 0 0

时间限制:1.0s   内存限制:256.0MB资源限制

问题描述

对于长度为5位的一个01串,每一位都可能是0或1,一共有32种可能。它们的前几个是:

00000

00001

00010

00011

00100

请按从小到大的顺序输出这32种01串。

输入格式

本试题没有输入。

输出格式

输出32行,按从小到大的顺序每行一个长度为5的01串。

样例输出

00000
00001
00010
00011
<以下部分省略>

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<algorithm>
  6. typedef long long ll;
  7. using namespace std;
  8. int num[100];
  9. int main()
  10. {
  11. for(int i=0; i<32; i++)
  12. {
  13. int cnt=0;
  14. int j=i;
  15. while(j)
  16. {
  17. num[cnt++] = j % 2;
  18. j = j/2;
  19. }
  20. for(int k=4; k>=0; k--)
  21. cout<<num[k];
  22. puts("");
  23. }
  24. }

 部分题解代码来自:https://blog.nowcoder.net/n/09f531a212f54db98f1aceee72468bfb?tdsourcetag=s_pctim_aiomsg

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/48220
推荐阅读
相关标签
  

闽ICP备14008679号