ZSTU 七月月赛 C D F

Problem C

每个数据记录属于哪个人和数值大小,按照数值大小排序。

确定我们目标数值在其中的区间

第一个目标值出现的位置记做pos_left,

最后一个目标值出现的位置记做pos_right,

我们所需这个数据出现的位置need_pos = (F*M+1)/2

如果pos_left>need_pos,对pos_left左边的数据统计各个人拥有的数量,先把数据最多的人用掉可以减少人数。

pos_right<need_pos,pos_right,对pos_right右边进行相同操作

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#define ll long long
#define for0(i,a,b) for (int i=a;i<b;i++)
#define for1(i,a,b) for (int i=a;i<=b;i++)

using namespace std;
#define mod 1007
const int N = 1e6+5;

struct node
{
    int v;
    int people;
    bool operator < (const node& a)const{
        return v < a.v;
    }
}p[N];
int v[N];
int cnt[1010];///记录所需区间内各个人的数据共出现几次

int main()
{
    //int T;scanf("%d",&T);
    int F,M,goal;
    while (~scanf("%d %d %d",&F,&M,&goal)){
        memset(cnt,0,sizeof cnt);
        for1(i,1,F*M){
            scanf("%d",&p[i].v);
            p[i].people = (i-1)/M+1;
        }
        sort(p+1,p+1+F*M);
        for1(i,1,F*M) v[i] = p[i].v;

        int need_pos = (F*M+1)/2;
        int pos_left = lower_bound(v+1,v+1+F*M,goal) - v;
        int pos_right = upper_bound(v+1,v+1+F*M,goal) - v - 1;
        if (need_pos>=pos_left && need_pos <= pos_right) {printf("0 0\n");continue;}

        //printf("pos_l=%d pos_r=%d\n",pos_left,pos_right);

        if (v[1]>goal) pos_left = pos_right = 0;
        if (v[F*M]<goal) pos_right ++;

        //printf("pos_l=%d pos_r=%d\n",pos_left,pos_right);

        int update_cnt;
        int ans=0,ans_cnt;///记录修改的人数,修改的总数
        if (pos_left>need_pos){/// 需要从左边去掉几个数
            update_cnt = pos_left-need_pos;
            ans_cnt = update_cnt;
            for1(i,1,pos_left-1) cnt[p[i].people]++;
            //for1(i,1,F) printf("第%d个人的数据个数=%d\n",i,cnt[i]);
            sort(cnt+1,cnt+F+1,[](int a,int b){return a>b;});
            for1(i,1,F){
                update_cnt -= cnt[i];
                ans++;
                if (update_cnt<=0) break;
            }
        }
        else if (pos_right<need_pos){///从右边去掉几个数
            update_cnt = need_pos-pos_right;
            ans_cnt = update_cnt;
            for1(i,pos_right+1,F*M) cnt[p[i].people]++;
            sort(cnt+1,cnt+F+1,[](int a,int b){return a>b;});
            for1(i,1,F){
                update_cnt -= cnt[i];
                ans++;
                if (update_cnt<=0) break;
            }
        }
        printf("%d %d\n",ans,ans_cnt);
    }
    return 0;
}

Problem D

现在只考虑1/8块,紧贴x轴正方向的上半块三角形,最后求出答案*8就行了

我们需要一条直线在范围内有C个整数点,由于整数点在直线上均匀分布,我们只需判断

我们将区间分为C+1部分,那么第一个点整数点x一定要大于n/(C+1),否则就至少有C+1个整数点了

将区间分为C个部分,那么第一个整数点又要小于等于n/C,否则就不够C个整数点了,

所以线段第一个点被限制在这个(n/(c+1),n/c]中

我们在这个区域找所有线段的第一个整数点,怎么样才能确保这个点是线段第一个整数点呢?

互质就可以了,然后问题就变成了对于x(n/(c+1),n/c]求每个x小于自身的互质的数的个数

用欧拉筛即可O(N)求出所有点小于自身的互质的数

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#define for0(i,a,b) for (int i=a;i<b;i++)
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define ll long long
#define mod 1000000007
using namespace std;

const int N = 5e6+5;

bool vis[N];
int prime[N],tot;
int phi[N];

void euler()
{
    tot = 0;
    phi[1] = 1;
    for1(i,2,5000000){
        if (!vis[i]){
            phi[i] = i-1;
            prime[tot++] = i;
        }
        for (int j=0;j<tot && i*prime[j]<=5000000;j++){
            vis[i*prime[j]] = true;
            if (i%prime[j]==0){
                phi[i*prime[j]] = phi[i]*prime[j];
                break;
            }
            else phi[i*prime[j]] = phi[i]*phi[prime[j]];
        }
    }
}

int main()
{
    euler();
    int n,c;
    scanf("%d %d",&n,&c);
    int minv = n/(c+1);
    int maxv = n/c;
    ll ans = 0;
    for (int i=minv+1;i<=maxv;i++)
        ans += phi[i];
    ans = ans*8;
    
    printf("%lld\n",ans);
    return 0;
}

Problem F

先选取一半人作为主场,此时共C\binom{n/2}{n}种方案,因为比赛是无序的,所以这取出的一半人就放在主场,接下去就是另一半人

去挑选和主场的谁比,此时是A\binom{n/2}{n/2}种,两个相乘就是A\binom{n/2}{n}

接下去反转,原先主场那批人现在去客场,等待对手来匹配,剩下一半人由于不能再找自己原来的对手,所以就变成了一个错排问题:(n/2)个人进行排序,每个人不能到和自己编号一样的位置,问共有多少种方案?

有个DP的公式:dp[i] = (i-1)*dp[i-1]+(i-1)*dp[i-2]

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#define for0(i,a,b) for (int i=a;i<b;i++)
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define ll long long
#define mod 1000000007
using namespace std;

const int N = 5e5+5;

ll dp[N];

void prework()
{
    dp[1] = 0;
    dp[2] = 1;
    for1(i,3,250000) dp[i] = (i-1)*(dp[i-1]+dp[i-2])%mod;
}

int main()
{
    prework();
    ll n;
    while (~scanf("%lld",&n)){
        ll ans = 1;
        for (ll i=n/2+1;i<=n;i++) ans = (ans*i)%mod;
        ans = ans*dp[n/2]%mod;
        printf("%lld\n",ans);
    }
    return 0;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值