问题 A: 分组
小明的班级在上体育课,老师想让大家分成两组玩拔河。现在大家排成了一排,老师嫌麻烦,所以想直接在队伍中间找个位置断开,这样不就分成了两组了吗?但是这个老师的思维很奇怪,他觉得分成人数相等的两组会不公平,他想让两组的体重和的差最小(差值要求体重和大的值减小的值)。老师把这个任务交给了小明,你能帮帮他吗?
输入
第一行为一个正整数n,表示小明班的人数。
第二行为n个正整数,表示这一排从左到右同学的体重。
输出
只有一行,为题目描述中最小的差值。
样例输入
5
11 17 3 2 20
样例输出
3
提示
【样例解释】
从第二位和第三位同学中间断开,则
第一位和第二位同学分为一组,体重和为11+17=28
第三位,第四位和第五位分为一组,体重和为3+2+20=25
28和25相差3,此时为最小,所以答案为3。
数据规模
60%的数据:2<=n<=1000,每个同学的体重不超过20。
100%的数据:2<=n<=100000,每个同学的体重不超过1000。
前缀和,减一减找最小
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#include<queue>
#include<map>
#include<set>
#include<math.h>
#include<vector>
#include<set>
#define INF 0x3f3f3f3f
#define LL long long
#define mem(a, b) memset(a, b, sizeof(a))
#define N 100001
using namespace std;
int n, a[N],pre[N];
int main() {
while(scanf("%d",&n)!=EOF) {
pre[0]=0;
for(int i=1; i<=n; i++) {
scanf("%d",&a[i]);
pre[i]=pre[i-1]+a[i];
}
int ans=0x3f3f3f3f;
for(int i=2; i<n; i++) {
ans=min(ans,abs(pre[n]-2*pre[i]));
}
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
倍数,比如例子,16=6, 23=6, 32=6, 41<=6,51<=6,61<=6,所以就t2(大的那个)除以每个数的和,减去t1前面的
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#include<queue>
#include<map>
#include<set>
#include<math.h>
#include<vector>
#include<set>
#define INF 0x3f3f3f3f
#define LL long long
#define mem(a, b) memset(a, b, sizeof(a))
#define N 10000001
#define MOD 1000000007
using namespace std;
int t1, t2;
int main() {
scanf("%d%d",&t1,&t2);
if(t1>t2) {
int t=t1;
t1=t2, t2=t;
}
LL ans=0;
for(int i=1; i<=t2; i++) {
ans+=t2/i;
}
for(int i=1; i<t1; i++) {
ans-=(t1-1)/i;
}
cout<<ans;
}
问题 C: 一脸严肃的声明: 作业作假的行为是可耻的行为!
一脸严肃的声明: 作业作假的行为是可耻的行为!
有F个好朋友正在做物理作业,每个朋友要做M个实验,他们的实验数据会保存在data数组里面 前M个数据是第一个人的,接下来M个数据是第二个人的,以此类推
一个序列的中位数是这个序列排序之后位于中间的那个数,如果序列的长度是偶数,中间两个数中较小的那个数就是中位数 {10,40,30,20}的中位数是20
根据实验手册的答案,实验数据的结果应该是goal,他们想要改变一些实验数据使得data数组的中位数为goal
返回以下两个数
1: 最少需要多少个人进行数据造假
2: 在最少人作假的前提下,最少需要改变多少个实验数据
输入
第一行输入三个正整数F,M,goal, 1 <= FM <=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}.
把每个人的大于目标值和小于目标值的个数记录下来,排序找到当前的中位数,然后找目标值的位置,计算要改多少个值才能符合
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#include<queue>
#include<map>
#include<set>
#include<math.h>
#include<vector>
#include<set>
#define INF 0x3f3f3f3f
#define LL long long
#define mem(a, b) memset(a, b, sizeof(a))
#define N 1000001
using namespace std;
int n, m, goal, a[N], mid, pos, peo;
int lower[1001], upper[1001];
int main() {
scanf("%d%d%d",&n, &m, &goal);
for(int i=1; i<=n*m; i++) {
scanf("%d",&a[i]);
if(a[i]<goal)
lower[(i-1)/m]++;
else if(a[i]>goal)
upper[(i-1)/m]++;
}
sort(lower,lower+n);
sort(upper, upper+n);
sort(a+1, a+1+m*n);
mid=a[(n*m+1)/2];
if(mid==goal){
cout<<0<<" "<<0<<endl;
return 0;
}
if(mid>goal) {
int find=upper_bound(a+1, a+1+m*n, goal)-a-1;
pos=(n*m+1)/2-find;
}
else {
int find=lower_bound(a+1,a+1+m*n, goal)-a-1;
pos=find-(n*m+1)/2+1;
}
int now=0, cnt=n-1;
if(goal>mid){
while(1){
now+=lower[cnt--];
peo++;
if(now>=pos){
cout<<peo<<" "<<pos<<endl;
return 0;
}
if(cnt<0) break;
}
}
else {
while(1) {
now+=upper[cnt--];
peo++;
if(now>=pos){
cout<<peo<<" "<<pos<<endl;
return 0;
}
if(cnt<0) break;
}
}
}
问题 D: 猫抓老鼠
一个平面直角坐标系上,有一只猫站在原点(0,0),
对于除原点外的每一个(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
欧拉线性筛
要横坐标x上最多每过(n/c=i)格找到一只猫,最少每过(n/(c+1)+1)格找到一只猫,找到少于c+1只猫。用欧拉函数求与i互质的个数,这样能确定纵坐标y,从而求出(x,y)的个数,由于把坐标轴划分了8个区域是对称的,求出一块*8
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#include<queue>
#include<map>
#include<set>
#include<math.h>
#include<vector>
#include<set>
#define INF 0x3f3f3f3f
#define LL long long
#define mem(a, b) memset(a, b, sizeof(a))
#define N 1000001
using namespace std;
int n, c;
LL phi(int n) {//欧拉函数,求与n互质的值的个数
LL ans=n;
for(int i=2; i<=sqrt(n); i++)
if(n % i==0) {
ans=ans/i*(i-1);
while(n%i==0) n/=i;
}
if(n>1) ans=ans/n*(n-1);
return ans;
}
int main() {
scanf("%d%d",&n, &c);
int t1=n/c, t2=n/(c+1)+1;
LL ans=0;
for(int i=t2; i<=t1; i++) {
ans+=phi(i);
}
printf("%lld\n",ans*8);
}
问题 F: 夏令营
有n位OI选手准备展开一些一对一的单挑比赛.n是个偶数,每位选手都有自己的主场
517编程委员会决定给n位选手组织一次夏令营,规则如下
1: 夏令营恰好有两轮比赛
2: 每位选手在每轮比赛中恰好参加一次
3: 每两位选手最多在比赛中相遇一次
4: 每位选手都在主客场恰好打了一次比赛
现在你作为517编程委员会的筹划人员,需要计算一下一共有多少种不同的比赛安排方案
由于方案数可能比较大,输出方案数对1e9+7取模的值
备注: 每一场比赛可以用一个有序对(A,B)表示 A表示主场选手,B表示客场选手
每一轮比赛是一些比赛的无序集合
整个夏令营的安排方案是两轮比赛的有序对
输入
输入一个正整数n, (4 <= n <= 500000)
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).
其中第二种方案和第三种方案可以通过交换轮次获得,但是他们算不同的方案
错排
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#include<queue>
#include<map>
#include<set>
#include<math.h>
#include<vector>
#include<set>
#define INF 0x3f3f3f3f
#define LL long long
#define mem(a, b) memset(a, b, sizeof(a))
#define N 500000
#define MOD 1000000007
using namespace std;
int n, a[N];
int dis(int x) {
a[1]=0, a[2]=1;
for(int i=3; i<=x/2; i++) {
a[i]=(LL)(i-1)*(a[i-1]+a[i-2])%MOD;
}
return a[x/2];
}
int main() {
scanf("%d",&n);
LL ans=1;
for(int i=n/2+1;i<=n;i++)
{
ans=ans*i%MOD;
}
ans=ans*dis(n)%MOD;
printf("%lld\n",ans);
}
问题 I: 最小生成树
给定结点数为n,边数为 m的带权无向连通图G,所有结点编号为1,2,…n 。
求G的最小生成树的边权和。
输入
第一行两个正整数n,m 。
之后的m行,每行三个正整数ui,vi,wi(1<=ui,vi<=n,0<=wi<=109),描述一条连接结点ui和vi,边权为wi的边。
输出
一个整数表示G 的最小生成树的边权和。
样例输入
7 12
1 2 9
1 5 2
1 6 3
2 3 5
2 6 7
3 4 6
3 7 3
4 5 6
4 7 2
5 6 3
5 7 6
6 7 1
样例输出
16
提示
数据范围与提示
1<= n <= 2105
0<=m <=5105
板子
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#include<queue>
#include<map>
#include<set>
#include<math.h>
#include<vector>
#include<set>
#define INF 0x3f3f3f3f
#define LL long long
#define mem(a, b) memset(a, b, sizeof(a))
#define N 500001
using namespace std;
int n, m, fa[200001];
struct rec{
int x, y, z;
}edge[N];
bool operator <(rec a, rec b) {
return a.z<b.z;
}
int get(int x) {
if(x==fa[x]) return x;
return fa[x]=get(fa[x]);
}
LL ans;
int main() {
scanf("%d%d",&n, &m);
for(int i=1; i<=m; i++) {
scanf("%d%d%d",&edge[i].x, &edge[i].y, &edge[i].z);
}
ans=0;
sort(edge+1, edge+1+m);
for(int i=1; i<=n; i++) fa[i]=i;
for(int i=1; i<=m; i++) {
int x=get(edge[i].x);
int y=get(edge[i].y);
if(x==y) continue;
fa[x]=y;
ans+=edge[i].z;
}
cout<<ans<<endl;
}