集训队日常训练20181110 DIV210 题解及AC代码
Posted bobhuang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了集训队日常训练20181110 DIV210 题解及AC代码相关的知识,希望对你有一定的参考价值。
4375: 孪生素数
Total Submit: 324 Accepted:91
Description
所谓孪生素数指的就是间隔为 2 的相邻素数,它们之间的距离已经近得不能再近了,就象孪生兄弟一样。最小的孪生素数是 (3, 5),在 100 以内的孪生素数还有 (5, 7), (11, 13), (17, 19), (29, 31), (41, 43), (59, 61) 和 (71, 73),总计有 8 组。但是随着数字的增大,孪生素数的分布变得越来越稀疏,寻找孪生素数也变得越来越困难。那么会不会在超过某个界限之后就再也不存在孪生素数了呢?
孪生素数有无穷多对!这个猜想被称为孪生素数猜想,至今没有被严格证明。但借助于计算机我们确实可以找到任意大数范围内的所有孪生素数对,接下来给定一个数k,求第k组孪生素数。
Input
输入数据有多组,每行为一个正整数k(1<= k <=10000)。
Output
输出有两个整数,以空格隔开,表示第k组孪生素数。
Sample Input
1
Sample Output
3 5
素数序列Pn,其中Pi+1-Pi=2为孪生素数(素数永远是人们着迷的话题,包括今年非常火的黎曼猜想)
我们可以知道,这个数并不多,但是让求的也不多,暴力走一波
1可以先预处理出这些数,判断x和x+2都是素数
Solution from kelenb
#include<stdio.h> #include<iostream> #include<math.h> using namespace std; bool x(int m) { int i; int t=sqrt(m); for(i=2;i<=t;i++) { if(m%i==0) return false; } return true; } int main() { int a[15000]; int i,j,n,k=0; for(i=3;i<1400000;i++) { if(x(i)&&x(i+2)) a[k++]=i; } while(scanf("%d",&n)!=EOF) { printf("%d %d ",a[n-1],a[n-1]+2); } }
2预处理素数表然后去保存答案的,这个当然要快一些
Solution from 1025044866
#include<cstdio> #include<cstring> #include<iostream> #include<string> #include<cmath> using namespace std; bool a[1300010]; int b[500000]; int h; struct didi { int x,y; } c[10010]; void pf() { a[0]=a[1]=true; for(int i=2; i<=sqrt(1300000); i++) { if(!a[i]) { for(int j=i+i; j<=1300000; j+=i) a[j]=true; } } h=0; for(int i=0; i<1300000; i++) { if(!a[i]) b[h++]=i; } int num=1; for(int i=1; i<h; i++) { if(b[i]-b[i-1]==2) { c[num].x=b[i-1]; c[num++].y=b[i]; if(num==10001) break; } } } int main() { pf(); int n; while(scanf("%d",&n)!=EOF) { printf("%d %d ",c[n].x,c[n].y); } }
5471: 数据结构实验--图的最小代价生成树
Total Submit: 62 Accepted:14
Description
求带权无向图的最小代价生成树。
Input
输入数据为多组,每组数据包含多行,第一行为2个整数n,e,n为图的顶点数,e为边数,接下来是e行,每行3个整数,前两个整数是一个顶点对,代表一条边所依附的两个顶点,第3个整数是边的权值。
所有值不超过20。
Output
请使用prim算法生成一棵生成树,并输出为生成树的各条边及其权值。格式见样例。
Sample Input
5 7
1 2 1
1 3 1
2 3 4
2 4 2
2 5 1
3 4 5
4 5 6
Sample Output
1 2 1
1 3 1
2 5 1
2 4 2
B题就是Prim算法,但是呢,需要去理解这个模板吧(Prim加点,Kruskal加边)
Solution from taozi
#include<bits/stdc++.h> using namespace std; const int N=105; const int INF=0x3f3f3f3f; int lowc[N],vis[N],G[N][N],fa[N]; int Prim(int n) { vis[1]=1; for(int i=2;i<=n;i++)lowc[i]=G[1][i],fa[i]=1; for(int i=2;i<=n;i++) { int minc=INF; int p=-1; for(int j=1;j<=n;j++) if(!vis[j]&&minc>lowc[j]) minc=lowc[j],p=j; printf("%d %d %d ",fa[p],p,minc); vis[p]=1; for(int j=1;j<=n;j++) if(!vis[j]&&lowc[j]>G[p][j]) lowc[j]=G[p][j],fa[j]=p; } } int main() { int n,e,u,v,w; while(scanf("%d%d",&n,&e)!=EOF) { memset(G,INF,sizeof(G)); memset(vis,0,sizeof(vis)); for(int i=0;i<e;i++) { scanf("%d%d%d",&u,&v,&w); G[u][v]=G[v][u]=w; } Prim(n); } return 0; }
5488: 石子归并II
N堆石子摆成一个环。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价。计算将N堆石子合并成一堆的最小代价。
例如: 1 2 3 4,有不少合并方法
1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19)
1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24)
1 2 3 4 => 1 2 7(7) => 3 7(10) => 10(20)
括号里面为总代价可以看出,第一种方法的代价最低,现在给出n堆石子的数量,计算最小合并代价。
Input
第1行:N(2 <= N <= 1000)
第2 - N + 1:N堆石子的数量(1 <= A[i] <= 10000)
Output
输出最小合并代价。
Sample Input
4
1
2
3
4
Sample Output
19
区间DP+四边形优化
属于拉题人强行引导题,比较困难,有时间的可以去学学 学习博客
solution from zdragon
#include <bits/stdc++.h> using namespace std; const int INF=0x3f3f3f3f; int dp[2005][2005]; int a[2005][2005],pre[2005][2005],s[2005][2005]; int main() { int n; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i][i]),a[i+n][i+n]=a[i][i]; for(int i=1;i<=n<<1;i++) for(int j=i;j<=n<<1;j++) pre[i][j]=pre[i][j-1]+a[j][j]; memset(dp,INF,sizeof dp); for(int i=1;i<=n<<1;i++) s[i][i]=i,dp[i][i]=0; for(int l=1;l<n;l++) for(int i=1;i+l<=n<<1;i++) { int j=i+l; for(int k=s[i][j-1];k<=s[i+1][j];k++) if(dp[i][j]>dp[i][k]+dp[k+1][j]+pre[i][j]) dp[i][j]=dp[i][k]+dp[k+1][j]+pre[i][j],s[i][j]=k; } int minn=INF; for(int i=1;i<=n;i++) minn=min(minn,dp[i][i+n-1]); printf("%d ",minn); return 0; }
4025: 游西湖
Total Submit: 149 Accepted:62
Description
游山玩水是zzd的爱好之一,一天,zzd和朋友一共N个人在西湖游玩游玩,看到西湖这么宽,大家都想划船到对岸去,但是一艘船只能容纳两个人,当然也可以一个人划的,像zzd这样的胖子一个人划空间比较宽敞,其中一人到了对岸还必须把船划回来,不然后面的人就要着急了,一个人过河的时间是自己花费的时间,,两个人同时过桥的时候,花费的时间为两个人中速度最慢的那个人花费的时间,如何设计一个方案,zzd和他的朋友尽快过桥?
Input
输入数据有多组
每组测试数据的第一行是一个整数N(1<=N<=1000)表示共有N个人要过河
每组测试数据的第二行是N个整数Si,表示此人过河所需要花时间。(0<Si<=100)
程序以N=0结束
Output
输出所有人过河的最短时间
Sample Input
4
1 2 5 10
0
Sample Output
17
样例解释:1、2过去,1回来,5、10过去,2回来,1、2过去
每次都是最小和次小过去,然后最小回来,然后两个大的过去,次小回来,这样两个大的就过去了,以此类推。
偶数个的话,最后留两个,奇数个的话,最后留三个,手动模拟一下即可。
solution from zdragon
#include <bits/stdc++.h> using namespace std; int a[1005]; int main() { int n; while(~scanf("%d",&n),n) { for(int i=0;i<n;i++) scanf("%d",&a[i]); sort(a,a+n); int ans=0; if(n&1) { for(int i=4;i<n;i+=2) ans+=a[i]+a[0]+2*a[1]; ans+=a[0]+a[1]+a[2]; } else { for(int i=3;i<n;i+=2) ans+=a[i]+a[0]+2*a[1]; ans+=a[1]; } printf("%d ",ans); } return 0; }
3659: 神奇的探险之旅
Total Submit: 106 Accepted:17
Description
我们正在设计这样的一款儿童探险游戏:游戏由很多故事场景组成,每个场景中都有一个问题,游戏根据玩家的回答将进入下一场景,经过巧妙的设计,我们保证在一次“探险旅行”中,不会重复的进入任何相同的场景,因此最终探险故事将根据玩家的选择结束在某个场景中。玩家总希望能够让自己的探险之旅尽可能的长,给定故事情节布局,请判断最长能够到达多少个场景?
我们对故事的m个场景进行编号(1~m),并且每次都是从编号为1开始探险之旅。
Input
输入数据有多组,每组数据的第一行位一个正整数n,表示数据的组数。
每组数据的第一行为一个正整数m(1<m<1000),表示故事场景的总数,接下来有m行,第i行描述了第i个场景的信息,包括3种情况:
(1)如果该行包含1个正整数j,则表示由第i个故事场景只能到达第j个场景;
(2)如果该行包含2个正整数j和k,则表示由第i个故事场景可以到达第j或者k个场景;
(3)如果该行包含1个字符串“ENDING”,表示故事在第i个场景结束;
Output
每组数据输出格式如下:
Case #x: y
x为测试数据编号,从1开始计算,y为一个整数,表示最多能经过的场景数量。
Sample Input
2
3
2 3
ENDING
ENDING
5
4 5
ENDING
2
3
4
Sample Output
Case #1: 2
Case #2: 5
转化为最短路,负权值跑最短路就是正权值跑最长路
Solution from taozi
#include<bits/stdc++.h> using namespace std; const int maxn=1005; int T,sum,n,o=1,d[maxn]; vector< vector<int> >G(maxn); int dij() { memset(d,0,sizeof d); queue<int>q; q.push(1); while(!q.empty()) { int u=q.front();q.pop(); for(auto v:G[u]) { if(d[v]>d[u]-1) { d[v]=d[u]-1; q.push(v); } } } return *min_element(d+1,d+1+n)*-1; } int main() { string s; cin>>T; while(T--) { cin>>n; cin.get(); for(int i=1;i<=n;i++) { G[i].clear(); getline(cin,s); if(s[0]==‘E‘)continue; stringstream ss(s); while(ss>>sum)G[i].push_back(sum); } printf("Case #%d: %d ",o++,dij()+1); } return 0; }
5271: 质因数的个数
Total Submit: 945 Accepted:319
Description
求正整数N(N>1)的质因数的个数。
相同的质因数需要重复计算。如120=2*2*2*3*5,共有5个质因数。
Input
可能有多组测试数据,每组测试数据的输入是一个正整数N,(1<N<10^9)。
Output
对于每组数据,输出N的质因数的个数。
Sample Input
Sample Output
Hint
注意:1不是N的质因数;若N为质数,N是N的质因数。
#include <stdio.h> #include <math.h> int main() { int t,i,j,n; while(~scanf("%d",&n)) { t=0; for(j=2; j<=sqrt(n); j++) { if(n%j==0) { while(n%j==0) { n/=j; t++; } } } if(n>1) t++; printf("%d ",t); } }
4859: 分数线划定
Total Submit: 104 Accepted:60
Description
世博会志愿者的选拔工作正在 A 市如火如荼的进行。为了选拔最合适的人才,A 市对所有报名的选手进行了笔试,笔试分数达到面试分数线的选手方可进入面试。面试分数线根据计划录取人数的150%划定,即如果计划录取m名志愿者,则面试分数线为排名第m*150%(向下取整)名的选手的分数,而最终进入面试的选手为笔试成绩不低于面试分数线的所有选手。
现在就请你编写程序划定面试分数线,并输出所有进入面试的选手的报名号和笔试成绩。
【样例说明】
m*150% = 3*150% = 4.5,向下取整后为4。保证4 个人进入面试的分数线为88,但因为88有重分,所以所有成绩大于等于88 的选手都可以进入面试,故最终有5 个人进入面试。
Input
第一行,两个整数n,m(5 ≤ n ≤ 5000,3 ≤ m ≤ n),中间用一个空格隔开,其中n 表示报名参加笔试的选手总数,m 表示计划录取的志愿者人数。输入数据保证m*150%向下取整后小于等于n。
第二行到第 n+1 行,每行包括两个整数,中间用一个空格隔开,分别是选手的报名号k(1000 ≤ k ≤ 9999)和该选手的笔试成绩s(1 ≤ s ≤ 100)。数据保证选手的报名号各不相同。
Output
第一行,有两个整数,用一个空格隔开,第一个整数表示面试分数线;第二个整数为进入面试的选手的实际人数。
从第二行开始,每行包含两个整数,中间用一个空格隔开,分别表示进入面试的选手的报名号和笔试成绩,按照笔试成绩从高到低输出,如果成绩相同,则按报名号由小到大的顺序输出。
Sample Input
6 3
1000 90
3239 88
2390 95
7231 84
1005 95
1001 88
Sample Output
88 5
1005 95
2390 95
1000 90
1001 88
3239 88
sort就完事
Solution from ljq969309068
#include<stdio.h> #include<algorithm> #include<string> using namespace std; struct node { int x,y; } a[10000]; bool cmp(node s,node d) { if(s.y==d.y) return s.x<d.x; else return s.y>d.y; } int main() { int n,i,j,t,k; scanf("%d%d",&n,&k); k=k*1.5; for(i=0; i<n; i++) { scanf("%d %d",&a[i].x,&a[i].y); } sort(a,a+n,cmp); int num=k; while(a[k-1].y==a[k].y) { k++; num++; } printf("%d %d ",a[k-1].y,num); for(i=0; i<k; i++) { printf("%d %d ",a[i].x,a[i].y); } }
4822: 计算系数
Total Submit: 42 Accepted:14
Description
给定一个多项式(ax + by)k,请求出多项式展开后xnym项的系数。
【数据范围】
对于 30%的数据,有0≤k≤10;
对于 50%的数据,有a = 1,b = 1;
对于 100%的数据,有0≤k≤1,000,0≤n, m≤k,且n + m = k,0≤a,b≤1,000,000。
Input
共一行,包含 5 个整数,分别为a,b,k,n,m,每两个整数之间用一个空格隔开。
Output
输出共 1 行,包含一个整数,表示所求的系数,这个系数可能很大,输出对10007 取模后的结果。
Sample Input
1 1 3 1 2
Sample Output
3
可以组合数,可以DP
Solution from BobHuang (我滚动了一下,卡下空间,学滚动可以到这里)
#include <stdio.h> #define K 10007 int f[2][1005]; int main() { int a,b,n,m,k,i,j; scanf("%d%d%d%d%d",&a,&b,&k,&n,&m); b%=K,a%=K; int op=1; f[1][0]=b,f[1][1]=a; for(i=2; i<=k; i++) { op^=1; for(j=0; j<=i&&j<=n; j++) { f[op][j]=f[op^1][j]*b%K; if(j) f[op][j]=(f[op][j]+f[op^1][j-1]*a)%K; } } printf("%d ",f[op][n]); return 0; }
Solution from Esquecer
#include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #include <ctime> #include <iostream> #include <algorithm> #include <sstream> #include <string> #include <vector> #include <queue> #include <stack> #include <map> #include <set> #include <utility> #include <bitset> using namespace std; #define LL long long #define pb push_back #define mk make_pair #define pill pair<int, int> #define mst(a, b) memset(a, b, sizeof a) #define REP(i, x, n) for(int i = x; i <= n; ++i) #define pi acos(-1.0) #define Max_N 1001 #define INF 2000000000 #define f(i,a,b) for(i = a;i<=b;i++) #define fi(i,a,b) for(i = a;i>=b;i--) #define LL long long using namespace std; #include<cstdio> const int N = 200000 + 5; const LL MOD = 10007LL; LL F[N], Finv[N], inv[N];//F是阶乘,Finv是逆元的阶乘 void init() { inv[1] = 1; for(int i = 2; i < N; i ++) { inv[i] = (MOD - MOD / i) * 1ll * inv[MOD % i] % MOD; } F[0] = Finv[0] = 1; for(int i = 1; i < N; i ++) { F[i] = F[i-1] * 1ll * i % MOD; Finv[i] = Finv[i-1] * 1ll * inv[i] % MOD; } } LL comb(int n, int m) //comb(n, m)就是C(n, m) { if(m < 0 || m > n) return 0; return F[n] * 1ll * Finv[n - m] % MOD * Finv[m] % MOD; } LL q(int a,int b) { LL ans = 1; LL di = a*1LL; while(b) { if(b&1) ans = (ans * di * 1LL)%MOD; di = di * di %MOD; b>>=1; } return ans%MOD; } int main() { init(); int a,b,k,n,m; scanf("%d %d %d %d %d",&a,&b,&k,&n,&m); cout<<((comb(k,n)*q(a,n))%MOD*q(b,m))%MOD<<endl; } /* 1 1 3 1 2 332223 133434 888 442 446 */
4507: 构造矩阵
Total Submit: 96 Accepted:60 Special Judge
Description
给定一个矩阵A,请构造一个对称矩阵B和反对称矩阵C,使得B+C=A。
如果满足MT=M,则称矩阵M为对称矩阵。
如果满足-MT=M,则称矩阵M为反对称矩阵。
MT称为M的转置矩阵。
Input
输入数据的第一行为一个正整数N(N<10),表示矩阵A的行数(或列数)。
接下来有N行,每行N个整数,表示矩阵的元素。
Output
先输出矩阵B,共有N数,每行有N个整数。
接下来输出一个空行。
再输出矩阵B,共有N行,每行有N个整数。
如果有多个结果,只要输出其中一个。
数据保证至少有一个解。
Sample Input
2
2 2
4 4
Sample Output
2 3
3 4
0 -1
1 0
比较神奇的构造
Solution from l1401642632
#include<iostream> #include<algorithm> using namespace std; int main() { int n,j,a[10][10],i; cin>>n; for(i=0; i<n; i++) { for(j=0; j<n; j++) { cin>>a[i][j]; } } for(i=0; i<n; i++) { for(j=0; j<n; j++) { if(j)cout<<" "; cout<<(a[i][j]+a[j][i])/2; } cout<<endl; } cout<<endl; for(i=0; i<n; i++) { for(j=0; j<n; j++) { if(j)cout<<" "; cout<<a[i][j]-(a[i][j]+a[j][i])/2; } cout<<endl; } }
3039: 材质贴图
Total Submit: 50 Accepted:21
Description
3D游戏中的场景经常用材质贴图来表现,例如石头、水面等。
通常,材质贴图是一张正方形的位图,上下边界的像素对应相同,左右边界的像素也对应相同。例如下图中,左边是一张材质贴图,而右边的不是(左右边界不同)。
给定一张n×n的位图,请在这张位图中寻找一块材质贴图,使得材质贴图尺寸最大。下图中黄色区域就是最大的材质贴图,虽然黄色区域左上角2×2的位图也是材质贴图,但不是最大的。
Input
输入包含多组数据。
每组数据第一行是一个整数n (1≤n≤50),表示位图的大小。
然后n行每行n个在0到255之间的整数,描述位图的内容。
输入数据以n=0结束,不要处理这组数据。
Output
对每组数据输出最大的材质贴图的边长。请注意,1×1的位图也是材质贴图。
Sample Input
2
255 0
0 127
5
5 251 127 11 195
23 13 0 13 23
211 0 13 0 67
211 13 0 13 23
1 251 127 11 47
0
Sample Output
1
3
往中间走,然后暴力枚举
Solution from BobHuang
#include<stdio.h> int n,a[51][51]; int la() { for(int k=n; k>2; k--) for(int i=0; i<=n-k; i++) for(int j=0; j<=n-k; j++) { int f=1; for(int l=0; l<k; l++) if(a[i][j+l]!=a[i+k-1][j+l]||a[i+l][j]!=a[i+l][j+k-1]) { f=0; break; } if(f)return k; } for(int i=0; i<=n-2; i++) for(int j=0; j<=n-2; j++) { if(a[i][j]==a[i+1][j+1]&&a[i+1][j]==a[i][j+1]) return 2; } return 1; } int main() { while(scanf("%d",&n),n) { for(int i=0; i<n; i++) for(int j=0; j<n; j++) scanf("%d",a[i]+j); printf("%d ",la()); } return 0; }
Solution from Nayo
#include<iostream> using namespace std; int main() { int t,a[51][51],x,y; while(cin>>t,t) { int sme=1; for(int i=0; i<t; i++) { for(int j=0; j<t; j++) { cin>>a[i][j]; } } for(int i=0; i<t; i++) { for(int j=0; j<t; j++) { x=i; y=j; x++; y++; while(x<t&&y<t) { if(a[i][j]==a[x][y]&&a[i][j]==a[i][y]&&a[i][j]==a[x][j]) { //cout<<x-i<<endl; int flag=0; for(int o=i; o<=x; o++) { if(a[o][y]!=a[o][j]) { flag=1; break; } } for(int o=j; o<=y; o++) { if(a[i][o]!=a[x][o]) { flag=1; break; } } if(flag==0&&x-i+1>sme) sme=(x-i)+1; } x++; y++; } } } cout<<sme<<endl; } }
以上是关于集训队日常训练20181110 DIV210 题解及AC代码的主要内容,如果未能解决你的问题,请参考以下文章
2021年暑假康复性训练(Codeforces Round #731 (Div. 3))全题解