浙江理工大学7月月赛

 比赛网址  http://47.96.116.66/contest.php?cid=1376

A题分组

 

题目描述

小明的班级在上体育课,老师想让大家分成两组玩拔河。现在大家排成了一排,老师嫌麻烦,所以想直接在队伍中间找个位置断开,这样不就分成了两组了吗?但是这个老师的思维很奇怪,他觉得分成人数相等的两组会不公平,他想让两组的体重和的差最小(差值要求体重和大的值减小的值)。老师把这个任务交给了小明,你能帮帮他吗?

输入

第一行为一个正整数n,表示小明班的人数。 
第二行为n个正整数,表示这一排从左到右同学的体重。

输出

只有一行,为题目描述中最小的差值。

样例输入

5
11 17 3 2 20

样例输出

3

#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);
}
View Code
 

   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
 
约数即因数,例如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的因数之和呢?想必已十分简单.
代码
 
#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);
}
View Code

 

问题 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}.
 
思路:准备两个数组,一个用来存储每个人比理想分数高的部分,另一个是比理想分数低的部分,然后排序,最后哪个人倒霉(要造假的数据多)就先上他了
0_o
代码:
 
#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);
    }
}
View Code

 

 

问题 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

  思路:先得搞清楚,在某个方向上,能抓到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°能抓到一样多的老鼠,故同样算作欧拉函数)。

 代码:

 

#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);
    }
}
View Code

 

问题 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).

其中第二种方案和第三种方案可以通过交换轮次获得,但是他们算不同的方案
 
好,现在开始一年一度的夏令营比赛,开始随机匹配选手,上路是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]。
 
代码 :
#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);
}
View Code

 

 

 
posted @ 2019-07-11 16:39  _HonEy  阅读(285)  评论(0编辑  收藏  举报