2025年2月17日月曜日

その他のA問題(ABC236-250)

ABC238-A

問題概要

2^n > n^2 か。

解答

n=gets.to_i
if n==1 || n>4 then
    puts "Yes"
else
    puts "No"
end
    

上がコンテスト中に提出したコード。(2022-02-05 21:11:21)

Cで書いたのが以下。(2024-12-10 11:39:44)

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 12
int main(void){
    char* s=(char*)malloc(sizeof(char)*N1);
    if (s==NULL){
        printf("memory allocation to s failed.\n");
        exit(1);
    }
    char *ts=fgets(s,N1,stdin);
    if(ts==NULL){
        printf("fgets(s) failed\n");
        exit(1);
    }
    int n=readint(s);
    free(s);
    if(n==1 || n>4){
        printf("Yes\n");
    }else{
        printf("No\n");
    }
    return 0;
}
int readint(char *s){
    int i=0,ret=0;
    while(*(s+i)>=48 && *(s+i)<=57){
//        printf("%d\n",*(s+i));
        ret*=10;
        ret+=*(s+i)-48;
        i+=1;
    }
    return ret;
}
    

グラフを書いてみると明らかなように、nが少し大きくなると指数関数の方が平方よりもずっと大きくなる。(高2の頃にやったことだ。実数 x について極限を取ると、a \gt 1 のとき、 \lim_{x \to ∞}\frac{a^x}{x^a} = ∞ である。)

n2^nn^2
121
244
389
41616
53225
66436
712849
825664
951281
101024100

n=10までを表にすると上のようになる。常に 2^n の方が大きくなるのは n=5 からだが、その前に n=1 の時にも 2^n の方が大きくなっていることに注意が必要。Rubyで書いたコードもCで書いたコードも上の表を念頭に場合分けして書いている。制約上nは最大で10^9なので、実際に2^nやn^2を計算するのはRubyでならともかくCでは無理。n=32で既に2^nはint型で扱える範囲を超えてしまうのだ。


確率

ABC242-A

問題概要

1からnまでの数がある。この数から、小さい順にa個選び、続いて、a+1からbまでの数のうちc個をランダムに選ぶ。ある数xが選ばれる確率を求めよ。

解答

a,b,c,x=gets.chomp.split(" ").map(&:to_i)
p=0.0
if x<=a
    p=1.0
elsif
    x>b
    p=0.0
else
    p=c/(b-a).to_f
end
puts p
    

上がまずコンテスト中に提出したコード。(2022-03-05 21:08:04)

ところで、この日はなぜかA問題でコンテスト中に同じRubyのコードを2回回提出している。その二番目が以下。(2022-03-05 21:09:46)

a,b,c,x=gets.chomp.split(" ").map(&:to_i)
p=0.0
if x<=a
    p=1.0
elsif
    x>b
    p=0.0
else
    p=c/(b-a).to_f
end
puts p
    

先のコードと比べると、xがa以下かbより大のときに代入する1と0が整数か小数かの違いだけなのだが、それだけで実行速度が61ms から 137ms へと大幅に遅くなっている。

Cで書いたのが以下。(2025-01-03 19:35:10)

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 21
int main(void){
    char* s=(char*)malloc(sizeof(char)*N1);
    if (s==NULL){
        printf("memory allocation to s failed.\n");
        exit(1);
    }
    char *ts=fgets(s,N1,stdin);
    if(ts==NULL){
        printf("fgets(s) failed\n");
        exit(1);
    }
    int a=readint(s);
    int i=0;
    while(*(s+i)!=32)i++;
    i++;
    int b=readint(s+i);
    i++;
    while(*(s+i)!=32)i++;
    i++;
    int c=readint(s+i);
    while(*(s+i)!=32)i++;
    i++;
    int x=readint(s+i);
    free(s);
//    printf("a=%d b=%d c=%d x=%d\n",a,b,c,x);
    double p=0;
    if (x<=a)p=1.0;
    else if (x<=b)p=(double)c/(b-a);
    printf("%lf\n",p);
    return 0;
}
int readint(char *s){
    int i=0,ret=0;
    while(*(s+i)>=48 && *(s+i)<=57){
//        printf("%d\n",*(s+i));
        ret*=10;
        ret+=*(s+i)-48;
        i+=1;
    }
    return ret;
}
    

周期算

ABC243-A

問題概要

初期値がVの数値を、3個単位周期の数列a,b,c,a,b,c,...に従い、k回目にはその数列の第k項の数を減算していく。数値が0未満になるのはいつか。(それぞれ数aを減算した時に0未満になるならばF,bを減算した時ならばM,cを減算した時ならばTを出力。)

解答

v,a,b,c=gets.chomp.split(" ").map(&:to_i)
t=a+b+c
v=v%t
if v<a
    puts "F"
elsif v<a+b
    puts "M"
else
    puts "T"
end
    

上が、コンテスト中に提出したコード。(2022-03-12 21:06:08)

Cで書いたのが以下。(2025-01-03 21:15:30 )

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 29
int main(void){
    char* s=(char*)malloc(sizeof(char)*N1);
    if (s==NULL){
        printf("memory allocation to s failed.\n");
        exit(1);
    }
    char *ts=fgets(s,N1,stdin);
    if(ts==NULL){
        printf("fgets(s) failed\n");
        exit(1);
    }
    int v=readint(s);
    int i=0;
    while(*(s+i)!=32)i++;
    i++;
    int a=readint(s+i);
    i++;
    while(*(s+i)!=32)i++;
    i++;
    int b=readint(s+i);
    while(*(s+i)!=32)i++;
    i++;
    int c=readint(s+i);
    free(s);
//    printf("a=%d b=%d c=%d v=%d\n",a,b,c,v);
    int t=a+b+c;
    int r=v%t;
    if (r-a<0)printf("F");
    else if (r-a-b<0)printf("M");
    else printf("T");
    return 0;
}
int readint(char *s){
    int i=0,ret=0;
    while(*(s+i)>=48 && *(s+i)<=57){
//        printf("%d\n",*(s+i));
        ret*=10;
        ret+=*(s+i)-48;
        i+=1;
    }
    return ret;
}
    


ABC245-A

問題概要

a時b分とc時d分1秒とでどちらが早い時刻か判定せよ。

解答

a=gets.chomp.split(" ").map(&:to_i)
ta=a[0]*3600+a[1]*60
ao=a[2]*3600+a[3]*60+1
if ta<ao 
    puts "Takahashi"
else
    puts "Aoki"
        end
    

これが、コンテスト中に提出したコード。(2022-03-26 21:06:19)

Cで書いたのが以下。(2025-01-04 23:42:51)


int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 21
int main(void){
    char* s=(char*)malloc(sizeof(char)*N1);
    if (s==NULL){
        printf("memory allocation to s failed.\n");
        exit(1);
    }
    char *ts=fgets(s,N1,stdin);
    if(ts==NULL){
        printf("fgets(s) failed\n");
        exit(1);
    }
    int a=readint(s);
    int i=0;
    while(*(s+i)!=32)i++;
    i++;
    int b=readint(s+i);
    i++;
    while(*(s+i)!=32)i++;
    i++;
    int c=readint(s+i);
    while(*(s+i)!=32)i++;
    i++;
    int d=readint(s+i);
    free(s);
//    printf("a=%d b=%d c=%d d=%d\n",a,b,c,d);
    int taka=3600*a+60*b;
    int ao=3600*c+60*d+1;
    if(taka<ao)printf("Takahashi\n");
    else printf("Aoki\n");
    return 0;
}
int readint(char *s){
    int i=0,ret=0;
    while(*(s+i)>=48 && *(s+i)<=57){
//        printf("%d\n",*(s+i));
        ret*=10;
        ret+=*(s+i)-48;
        i+=1;
    }
    return ret;
}
    


場合分け

ABC249-A

問題概要

  1. 甲は「A 秒間 B (m/s)で進み、C 秒間停止する」ことを繰り返す。
  2. 乙は「D 秒間 E (m/s)で進み、F 秒間停止する」ことを繰り返す。
甲、乙が同時に出発してからX秒後、どちらがより大きな距離を進んでいるか。

解答

a,b,c,d,e,f,x=gets.chomp.split(" ").to_a.map(&:to_i)
taka=0
aoki=0
n1,r1=x.divmod(a+c)
if r1>=a
    taka=(n1+1)*a*b
else
    taka=(n1*a+r1)*b
end
t2=0
n2,r2=x.divmod(d+f)
if r2>=d
    aoki=(n2+1)*d*e
else
    aoki=(n2*d+r2)*e
end
#p [taka,aoki]
if taka==aoki
    puts "Draw"
elsif taka>aoki
    puts "Takahashi"
else
    puts "Aoki"
end    
    

上がコンテスト中に提出したコード。(2022-04-23 21:25:57)

Cで書いたコードは以下。(2025-02-12 22:50:12)

#define N1 29
int readint(char *s);
int min(int a,int b);
#include<stdio.h>
#include<stdlib.h>
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    if(s1==NULL){
        printf("memory allocation to s1 failed\n");
        exit(1);
    }
    fgets(s1,N1,stdin);
    int a=readint(s1);
    int i=0;
    while(*(s1+i)!=32)i++;
    i++;
    int b=readint(s1+i);
    while(*(s1+i)!=32)i++;
    i++;
    int c=readint(s1+i);
    while(*(s1+i)!=32)i++;
    i++;
    int d=readint(s1+i);
    while(*(s1+i)!=32)i++;
    i++;
    int e=readint(s1+i);
    while(*(s1+i)!=32)i++;
    i++;
    int f=readint(s1+i);
    while(*(s1+i)!=32)i++;
    i++;
    int x=readint(s1+i);
//    printf("a=%d b=%d c=%d d=%d e=%d f=%d x=%d\n",a,b,c,d,e,f,x);
    int q1=x/(a+c); int r1=x%(a+c);
    int q2=x/(d+f); int r2=x%(d+f);
    int takahashi=q1*a*b+min(r1,a)*b;
    int aoki=q2*d*e+min(r2,d)*e;
//    printf("Takahashi=%d\n",takahashi);
//    printf("Aoki=%d\n",aoki);
    if(takahashi>aoki){
        printf("Takahashi\n");
    }else if(takahashi==aoki){
        printf("Draw\n");
    }else{
        printf("Aoki\n");
    }
    return 0;
}
int readint(char *s){
    int i=0,ret=0;
    while(*(s+i)>=48 && *(s+i)<=57){
        ret*=10;
        ret+=*(s+i)-48;
        i+=1;
    }
    return ret;
}
int min(int a,int b){
    if(a<b)return a;
    else return b;
}
    

中学受験の周期算でも割とあるパターンの問題なのだが、こういう、「進む」「休む」を繰り返す場合には、進んだ時間と休んだ時間をまとめて一周期として、これが何周期あるかを考える。まず甲について、進んだ距離を計算する。全体の時間xを一周期の時間(a+c)で割り、商がnになったとして、余りの時間が一周期に進む時間a以上ならば、全体としてn+1周期分進んでいることになる。余りがaよりも小さければ、それは端数の時間として秒単位で加算して、全体でn周期分+(x%(a+c))秒間進んでいることになる。最後はこの秒数に秒速をかければ進んだ距離が求められる。乙についても同様に進んだ距離を求め、両者を比較すればよい。