七月月赛(补)

问题 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 <=5
105

板子

#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;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值