比赛网址 http://47.96.116.66/contest.php?cid=1376
A题:分组
题目描述
小明的班级在上体育课,老师想让大家分成两组玩拔河。现在大家排成了一排,老师嫌麻烦,所以想直接在队伍中间找个位置断开,这样不就分成了两组了吗?但是这个老师的思维很奇怪,他觉得分成人数相等的两组会不公平,他想让两组的体重和的差最小(差值要求体重和大的值减小的值)。老师把这个任务交给了小明,你能帮帮他吗?
输入
第一行为一个正整数n,表示小明班的人数。
第二行为n个正整数,表示这一排从左到右同学的体重。
第二行为n个正整数,表示这一排从左到右同学的体重。
输出
只有一行,为题目描述中最小的差值。
样例输入
5
11 17 3 2 20
样例输出
3
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<cstdlib> #include<map> #include<vector> const int N=1e5+5; int sum1,sum2,a[N]; int n,i,j; using namespace std; int main() { int ans=0x3f3f3f; sum1=0; sum2=0; scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%d",&a[i]); sum1+=a[i]; } for(i=1;i<=n;i++) { sum2+=a[i]; ans=min(ans,abs(sum2-sum1+sum2)); } printf("%d\n",ans); }
B题:数学老师的难题
题目描述
学好数学是学好计算机的一个重要基础,毕竟早期计算机科学是数学科学的一个重要分支。帅帅也明白这个道理,因此,帅帅在学校既参加了数学兴趣班也参加了计算机兴趣班。但帅帅马上就要参加2010年东莞市小学程序设计竞赛了,因此就暂停了数学兴趣班的学习,数学老师知道后很生气,后果很严重,于是数学老师就给帅帅出了一个难题:统计两个正整数t1 、 t2 ( t1 不一定大于 t2 ) 之间的所有数的约数个数和S,这本是一个很简单的题目,但数学老师为了难倒帅帅,给出的两个数t1、t2的非常大,为10000000以内的正整数。
但帅帅的编程能力还处于入门阶段,希望你能帮他解决这个问题?
但帅帅的编程能力还处于入门阶段,希望你能帮他解决这个问题?
输入
输入文件problem.in仅包含一行,共有两个整数,表示t1 t2 (用空格分开)
输出
输出文件仅有一个整数,表示t1 , t2之间的约数个数之和
样例输入
2 6
样例输出
13
提示
2的约数有1,2共2个;
3的约数有1,3共2个;
4的约数有1,2,4,共3个;
5的约数有1,5共2个;
6的约数有1,2,3,6,;共4个。
所以2到6的约数个数为13
【数据规模】
对于50%的数据保证有t1,t2<=5000000
对于全部的数据保证有t1,t2<=10000000
3的约数有1,3共2个;
4的约数有1,2,4,共3个;
5的约数有1,5共2个;
6的约数有1,2,3,6,;共4个。
所以2到6的约数个数为13
【数据规模】
对于50%的数据保证有t1,t2<=5000000
对于全部的数据保证有t1,t2<=10000000
约数即因数,例如4的约数有1,2,4。如果是要求出一个数具体的约数,只能用for/while循环,复杂度不会超过O(n),但是本题还要求出区间内数的约数和,数与数之间的关系也不是很紧密,无法简化运算,很可能会超时。但若是改为仅求约数的个数,就可以另辟蹊径。设一个区间[1,N],显然,这些数的因数都含有1,即因数1的个数有N/1个。而含有因数为2的数有2,4,6...2*k...,即因数2的个数有N/2,依此类推,因数3的个数有N/3...因数K的个数有N/K,所以,重点来了!区间[1,N]的所有因数之和是多少?没错,就是$\sum_{i=1}^{N}\frac{N}{i}$ ;我们知道了1到N的因数之和,怎么求N到M的因数之和呢?想必已十分简单.
代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<cstdlib> #include<map> #include<vector> long long n,m,i,j; long long sum2,sum1; using namespace std; int main() { sum1=0; sum2=0; scanf("%lld%lld",&n,&m); long long maxx,minn; minn=min(n,m); maxx=max(n,m); n=minn; m=maxx; for(i=1;i<n;i++) { sum1+=(n-1)/i; } for(i=1;i<=m;i++) { sum2+=m/i; } //printf("%d\n",sum1); //printf("%d\n",sum2); printf("%lld\n",sum2-sum1); }
问题 C: 一脸严肃的声明: 作业作假的行为是可耻的行为!
题目描述
一脸严肃的声明: 作业作假的行为是可耻的行为!
有F个好朋友正在做物理作业,每个朋友要做M个实验,他们的实验数据会保存在data数组里面 前M个数据是第一个人的,接下来M个数据是第二个人的,以此类推
一个序列的中位数是这个序列排序之后位于中间的那个数,如果序列的长度是偶数,中间两个数中较小的那个数就是中位数 {10,40,30,20}的中位数是20
根据实验手册的答案,实验数据的结果应该是goal,他们想要改变一些实验数据使得data数组的中位数为goal
返回以下两个数
1: 最少需要多少个人进行数据造假
2: 在最少人作假的前提下,最少需要改变多少个实验数据
有F个好朋友正在做物理作业,每个朋友要做M个实验,他们的实验数据会保存在data数组里面 前M个数据是第一个人的,接下来M个数据是第二个人的,以此类推
一个序列的中位数是这个序列排序之后位于中间的那个数,如果序列的长度是偶数,中间两个数中较小的那个数就是中位数 {10,40,30,20}的中位数是20
根据实验手册的答案,实验数据的结果应该是goal,他们想要改变一些实验数据使得data数组的中位数为goal
返回以下两个数
1: 最少需要多少个人进行数据造假
2: 在最少人作假的前提下,最少需要改变多少个实验数据
输入
第一行输入三个正整数F,M,goal, 1 <= F
M <=1000, 0 <= goal <= 99 接下来一行输入FM个数表示data数组, 每个元素的范围0 <= data[i] <= 99
输出
输出一行包含两个整数
样例输入
【输入样例1】
5 5 8
1 2 3 4 5 10 9 8 7 6 25 24 23 22 21 18 16 17 19 20 11 13 12 14 15
【输入样例2】
4 3 12
3 8 12 3 8 12 3 8 12 8 12 17
样例输出
【输出样例1】
1 5
【输出样例2】
1 2
提示
有5个朋友,每个人有5个数据,一共是25个数据
当前的中位数是13,为了使其降到8, 第四个人可以改动实验数据
从{18,16,17,19,20} 到 {3,1,3,5,7}.
当前的中位数是13,为了使其降到8, 第四个人可以改动实验数据
从{18,16,17,19,20} 到 {3,1,3,5,7}.
思路:准备两个数组,一个用来存储每个人比理想分数高的部分,另一个是比理想分数低的部分,然后排序,最后哪个人倒霉(要造假的数据多)就先上他了
0_o
代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<iostream> #include<cstring> #include<algorithm> #include<cstdio> #include<vector> #include<cmath> using namespace std; const int N=1e6+5; int data[N],up[N],down[N]; int i,j,n,f,m,goal; int median; int needc; int ans,num; int main() { memset(down,0,sizeof down); memset(up,0,sizeof up); scanf("%d%d%d",&f,&m,&goal); for(i=1;i<=f*m;i++) { scanf("%d",&data[i]); if(data[i]<goal) { down[(i-1)/m]++; } else if(data[i]>goal) { up[(i-1)/m]++; } } sort(data+1,data+1+f*m); sort(up,up+f); sort(down,down+f); median=data[(1+f*m)/2]; if(median==goal) printf("0 0\n"); else { if(median>goal) { needc=(1+f*m)/2-(upper_bound(data+1,data+1+f*m,goal)-data-1); } else { needc=lower_bound(data+1,data+1+f*m,goal)-data-1-(1+f*m)/2+1; } //printf("%d\n",median); //printf("%d\n",needc); num=needc; ans=0; if(median>goal) { while(needc>0) { needc-=up[f-1]; f--; ans++; } } else if(goal>median) { while(needc>0) { needc-=down[f-1]; f--; ans++; } } printf("%d %d\n",ans,num); } }
问题 D: 猫抓老鼠
题目描述
一个平面直角坐标系上,有一只猫站在原点(0,0),
对于除原点外的每一个(x,y)(|x| <= N , |y| <= N)的位置上都有一只老鼠
假如猫选择了某个方向出击,那么它可以吃掉这个方向上的所有碰见的老鼠
现在问你有多少个方向上恰好有C只老鼠
对于除原点外的每一个(x,y)(|x| <= N , |y| <= N)的位置上都有一只老鼠
假如猫选择了某个方向出击,那么它可以吃掉这个方向上的所有碰见的老鼠
现在问你有多少个方向上恰好有C只老鼠
输入
输入两个整数N, C (1 <= N <= 5000000, 1 <= C <= N)
输出
输出一个整数
样例输入
【样例输入1】
2 2
【样例输入2】
2 1
【样例输入3】
1234 3
样例输出
【样例输出1】
8
【样例输出2】
8
【样例输出3】
180608
思路:先得搞清楚,在某个方向上,能抓到C只老鼠(不包括原点),则每只老鼠横坐标的间隔范围是多少?考虑两个极值:1,恰好第C只老鼠的横坐标为N,则distance=N/C。2,恰好第C+1只老鼠的横坐标为N(无法取到),则distance=N/(C+1);所以distance∈[N/C,N/(C+1))。我们先随便确定一个K∈[N/C,N/(C+1)),则K可以作为第一只抓到的老鼠的横坐标,这只老鼠的纵坐标可能大于K吗?不能,否则第C只老鼠的纵坐标将会大于N,所以第一只老鼠的Y<=X,那么,X和Y能有除1以外的因数吗?也不能。例如:假设X和Y有公因数2,则(X/2,Y/2)这个点的老鼠肯定会在之前被抓,还有(3X/2,3Y/2)的老鼠...这意味着如果X和Y有公因数(不为1)的话,猫抓到的老鼠是大于C的,即所有Y值的可能为<X的与X互质的数的个数,即X的欧拉函数,最后考虑坐标系的对称性*8即可(还有X==Y的特殊情况,这种情况下猫在沿X轴和斜45°能抓到一样多的老鼠,故同样算作欧拉函数)。
代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; int n,m,M,C; const int N=5e6+5; long long oula[N]; long long prime[N]; long long vis[N]; int a,b; void init(long long x) { memset(vis,0,sizeof vis); long long num=0; for(long long i=2;i<x;i++) { if(!vis[i]) { prime[num]=i; num++; oula[i]=i-1; } for(long long j=0;j<num&&i*prime[j]<x;j++) { vis[i*prime[j]]=1; if(i%prime[j]==0) { oula[i*prime[j]]=prime[j]*oula[i]; break; } else { oula[i*prime[j]]=oula[i]*(prime[j]-1); } } } } int main() { while(scanf("%d%d",&M,&C)!=EOF) { int t1=M/C; int t2=M/(C+1)+1; long long sum=0; //printf("%d %d\n",t1,t2+1); init(t1+1); oula[1]=1; for(int i=t2;i<=t1;i++) { sum+=oula[i]; } printf("%lld\n",sum*8); } }
问题 F: 夏令营
题目描述
有n位OI选手准备展开一些一对一的单挑比赛.n是个偶数,每位选手都有自己的主场
517编程委员会决定给n位选手组织一次夏令营,规则如下
1: 夏令营恰好有两轮比赛
2: 每位选手在每轮比赛中恰好参加一次
3: 每两位选手最多在比赛中相遇一次
4: 每位选手都在主客场恰好打了一次比赛
现在你作为517编程委员会的筹划人员,需要计算一下一共有多少种不同的比赛安排方案
由于方案数可能比较大,输出方案数对1e9+7取模的值
备注: 每一场比赛可以用一个有序对(A,B)表示 A表示主场选手,B表示客场选手
每一轮比赛是一些比赛的无序集合
整个夏令营的安排方案是两轮比赛的有序对
517编程委员会决定给n位选手组织一次夏令营,规则如下
1: 夏令营恰好有两轮比赛
2: 每位选手在每轮比赛中恰好参加一次
3: 每两位选手最多在比赛中相遇一次
4: 每位选手都在主客场恰好打了一次比赛
现在你作为517编程委员会的筹划人员,需要计算一下一共有多少种不同的比赛安排方案
由于方案数可能比较大,输出方案数对1e9+7取模的值
备注: 每一场比赛可以用一个有序对(A,B)表示 A表示主场选手,B表示客场选手
每一轮比赛是一些比赛的无序集合
整个夏令营的安排方案是两轮比赛的有序对
输入
输入一个正整数n, (4 <= n <= 500000)
n 是个偶数
n 是个偶数
输出
输出一个整数
样例输入
【样例输入1】
4
【样例输入2】
8
样例输出
【样例输出1】
12
【样例输出2】
15120
提示
样例一解释:
假设四位选手分别用A B C D表示,(A,B)表示A是主场,B是客场
其中的三种方案如下
第一轮比赛: (A,B), (C,D). 第二轮比赛: (B,C), (D,A).
第一轮比赛: (A,B), (D,C). 第二轮比赛: (B,D), (C,A).
第一轮比赛: (C,A), (B,D). 第二轮比赛: (A,B), (D,C).
其中第二种方案和第三种方案可以通过交换轮次获得,但是他们算不同的方案
假设四位选手分别用A B C D表示,(A,B)表示A是主场,B是客场
其中的三种方案如下
第一轮比赛: (A,B), (C,D). 第二轮比赛: (B,C), (D,A).
第一轮比赛: (A,B), (D,C). 第二轮比赛: (B,D), (C,A).
第一轮比赛: (C,A), (B,D). 第二轮比赛: (A,B), (D,C).
其中第二种方案和第三种方案可以通过交换轮次获得,但是他们算不同的方案
好,现在开始一年一度的夏令营比赛,开始随机匹配选手,上路是N/2个坑,下路也是这样,现在先选择一半倒霉的人丢进上面的坑,方法数是$C_{N}^{N/2}\textrm{}$,还有一半的人怎么办?自己选择个坑跳,方法数是$\Lambda _{N/2}^{N/2}$ ,然后两个坑的人换一下坑,原来在坑里对峙的人不能再次对峙,得换个人,这相当于一个错排,所以N个人的总跳坑数为$C_{N}^{N/2}\textrm{}$ × $\Lambda _{N/2}^{N/2}$ × DP[N/2]。
代码 :
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<iostream> #include<cstring> #include<algorithm> #include<cstdio> #include<vector> #include<cmath> using namespace std; const int mod=1e9+7; const int N=5e5+5; long long a[N]; int n,i,j,m; void DP(int x) { for(i=3;i<=x;i++) { a[i]=(i-1)*(a[i-1]+a[i-2])%mod; } return ; } int main() { a[1]=0; a[2]=1; scanf("%d",&n); m=n/2; DP(m); long long ans=1; for(i=n/2+1;i<=n;i++) { ans=ans*i%mod; } ans=ans*a[m]%mod; printf("%lld\n",ans); }