Codeforces Round #730 (Div. 2) 题解(A-D1)

本文通过四个具体的编程竞赛题目,展示了数学思维和高效算法在解决复杂问题中的关键作用。A题中,解释了如何通过分析gcd(a, b)的最大值及其影响因素快速求解。B题涉及高速公路的阻塞程度优化,通过均匀分配车辆来降低阻塞。C题探讨了概率变化下的期望计算,利用递归和动态规划求解。D题为交互式编程挑战,通过异或运算策略找到最佳猜测序列。这些实例揭示了数学和算法在编程中的重要地位。
摘要由CSDN通过智能技术生成

Codeforces Round #730 (Div. 2) 题解(A-D1)

A. Exciting Bets

题目大意:

给出两个整数 a a a b b b,可以通过不断地同时对 a a a b b b加一或者减一(最低减为0),问在此过程中最大的 g c d ( a , b ) gcd(a,b) gcd(a,b)和使得 a a a b b b的最大公约数达到最大时需要的最小操作次数。

解题思路:

因为 a a a b b b是同时增加和减少的,所以 a a a b b b之间的差值是固定的。设d为gcd(a,b),那么显然d也是 ∣ a − b ∣ |a-b| ab的因数,所以d最大能取到的就是 ∣ a − b ∣ |a-b| ab

同时,我们可以发现当 g c d ( a , b ) gcd(a,b) gcd(a,b)取最大值时,即 g c d ( a , b ) = ∣ a − b ∣ gcd(a,b)=|a-b| gcd(a,b)=ab时, a a a b b b一定是 ∣ a − b ∣ |a-b| ab的倍数,所以最小操作步数就是a或者b到最近的 k × ∣ a − b ∣ k\times|a-b| k×ab( k k k为自然数)的步数。

还要加一个特判,当 a = b a=b a=b的时候 g c d ( a , b ) gcd(a,b) gcd(a,b)能取到无穷大。

其实这种题如果当场想不清楚的话可以根据样例猜猜结论,样例能过一般就能过了。

代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
LL a,b;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%lld%lld",&a,&b);
        if(a<b) swap(a,b);
        LL c=a-b;
        if(c==0) puts("0 0");
        else{
            LL d=min(b%c,c-b%c);
            printf("%lld %lld\n",c,d);
        }
    }
    return 0;
}

B. Customising the Track

题目大意:

一个长度为 n n n数组 a a a表示高速公路上有 n n n个子轨道,其中 a i a_i ai表示第 i i i个子轨道上车的数量。

给出以下定义,高速公路的阻塞程度= ∑ i = 1 n ∑ j = i + 1 n ∣ a i − a j ∣ \sum\limits_{i=1}^n\sum\limits_{j=i+1}^n|a_i-a_j| i=1nj=i+1naiaj。现在允许执行以下操作任意次:将某一辆车从一个子轨道移到另一个子轨道。

问高速公路的阻塞程度最低能达到多少。

解题思路:

其实很容易就想到将所有的车辆均匀分配个 n n n个子轨道,最好的情况就是每个子轨道的车辆数都一样,其次就是会有一部分车辆多出来使得一部分子轨道比另一部分多一辆车,假设有 a a a个子轨道的车辆数是 k + 1 k+1 k+1,那么就有 n − a n-a na个子轨道的车辆数是 k k k,所以很显然阻塞程度就是 k × ( k + 1 ) k\times(k+1) k×(k+1)

至于为什么这样构造能保证正确性,我也不会完整的证明,CF官方题解中有详细的证明。简要地说说我的理解:如果成功按这种方式分配之后,任何使得当前结构发生变化的操作都会使得阻塞程度增大。

代码:
#include<cstdio>
using namespace std;
typedef long long LL;
int n;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        LL sum=0;
        for(int i=0;i<n;i++){
            int t;
            scanf("%d",&t);
            sum+=t;
        }
        LL a=sum%n;
        printf("%lld\n",a*(n-a));
    }
    return 0;
}

C. Need for Pink Slips

题目大意:

一共有三种纸条 C , M , P C,M,P C,M,P,起始每种纸条抽中的概率分别是 c c c, m m m, p p p。在没有抽到 P P P纸条之前可以一直抽,抽到 P P P纸条游戏就结束了。

如果没有抽到 P P P纸条并且当前这种纸条抽中之前的概率是 a a a,那么:

  1. 如果 a ≤ v a\leq v av,那么这种纸条之后抽到的概率变成0,这种纸条不再是一个有效的纸条,并且将减少的概率 a a a平均分配给剩下的有效纸条。
  2. 如果 a > v a\gt v a>v,那么这种纸条之后抽到的概率减少v,并且将减少的概率v平均分配给剩下的有效纸条。

给出 c , m , p , v c,m,p,v c,m,p,v,问抽到纸条数量的期望。

解题思路:

这道题其实难就难在读题,把题目读懂之后做起来是很简单的。

要注意当一种纸条无效之后是不能再分配概率的,除此之外再处理一下浮点数的精度问题,数据范围很小,直接爆搜出所有情况就可以了。

代码:
#include<bits/stdc++.h>
using namespace std;
const double eps=1e-6;
double sum;
void dfs(double c,double m, double p, double v,int cnt,double val){
    if(c>eps){
        double t=min(c,v);
        if(m>eps) dfs(c-t,m+t/2,p+t/2,v,cnt+1,val*c);
        else dfs(c-t,m,p+t,v,cnt+1,val*c);
    }
    if(m>eps){
        double t=min(m,v);
        if(c>eps) dfs(c+t/2,m-t,p+t/2,v,cnt+1,val*m);
        else dfs(c,m-t,p+t,v,cnt+1,val*m);
    }
    val*=p;
    cnt++;
    sum+=cnt*val;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        double c,m,p,v;
        scanf("%lf%lf%lf%lf",&c,&m,&p,&v);
        sum=0;
        dfs(c,m,p,v,0,1);
        printf("%.8f\n",sum);
    }
    return 0;
}

D1. RPD and Rap Sheet (Easy Version)

题目大意:

交互题,有一个范围在 [ 0 , n − 1 ] [0,n-1] [0,n1]内的初始密码,最多猜n次,密码猜正确返回1,错误返回0。如果这次密码猜测错误了,密码就会发生变化。假设猜测之前的密码是 x x x,你猜的密码是 y y y,那么系统就会把密码改成 z z z,使得 x ⊕ k z = y x\oplus_k z=y xkz=y(在easy版本中k只会等于2)。 x ⊕ k y = z x\oplus_ky=z xky=z的运算规则是将 x x x y y y转换为k进制,对于每一位上 ( x i + y i ) % k = z i (x_i+y_i)\%k=z_i (xi+yi)%k=zi,例如 5 ⊕ 3 5 = ∣ 12 ∣ 3 ⊕ 3 ∣ 12 ∣ 3 = ∣ 21 ∣ 3 = 7 5\oplus_35=|12|_3\oplus_3|12|_3=|21|_3=7 535=1233123=213=7

解题思路:

因为easy版本中 k = 2 k=2 k=2,所以所有的运算都是位运算中的异或运算。又因为异或运算是自反的,所以 x ⊕ z = y ⇒ x ⊕ y = z x\oplus z=y \Rightarrow x\oplus y=z xz=yxy=z

我们可以考虑从 0 0 0一直猜到 n − 1 n-1 n1,虽然在猜的过程中,猜错的话密码会发生变化,但我们在猜的过程中与密码采取相同的变化,即每次猜测之前都与迄今为止所有猜过数字的异或和异或一下,而要实现这一想法只要拿一个变量存一下过程中的异或和就行。

代码如下:
#include<cstdio>
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        int n,k;
        scanf("%d%d",&n,&k);
        int cur=0,t;
        for(int i=0;i<n;i++){
            printf("%d\n",i^cur);
            fflush(stdout);
            cur^=(i^cur);
            scanf("%d",&t);
            if(t==1) break;
        }
    }
    return 0;
}

D2. RPD and Rap Sheet (Hard Version)(待补)

题目大意:

与d1题意一致,只是 k k k不局限于2.

解题思路:

k k k进制的异或运算并不是自反的,所以d1的做法不用于这道题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值