2022年11月28日月曜日

配列の検索、インデックスを取得など

最大値のインデックスをすべて取得

ABC252-B

問題概要

数列aの最大値が、数列bに含まれる数をインデックスとしているか。

解答

n,k=gets.chomp.split(" ").to_a.map(&:to_i)
a=gets.chomp.split(" ").to_a.map(&:to_i)
b=gets.chomp.split(" ").to_a.map(&:to_i)
h=Hash.new
a.each do |item|
    if h.has_key?(item)
        h[item]+=1
    else
        h[item]=1
    end
end
amax=h.keys.max
amax_idx=[]
a.each_with_index do |item,i|
    amax_idx.push(i) if item==amax
end
b.each do |item|
    amax_idx.each do |idx|
        if item==idx+1
            puts "Yes"
            exit
        end
    end
end
puts "No"
    

「正の字カウント」で、aの各値の出現回数を求める。(今回は出現回数自体は必要ないのだが、既存のコードの流用のため。)そして、もう一度ループを回して、aの最大値のインデックスを配列amax_idxを作る。あとは、amax_idxとbに共通する要素があるか見ていけばよい。

Cで書いたのが以下。

#include<stdio.h>
#include<stdlib.h>
#define MAX 2e9+2
void print_ivector(int* a,int n);
int main(void){
    int n,m;
    int min_idx,max_idx,min=MAX,max=0;
    int max_count=0,min_count=0;
    scanf("%d %d\n",&n,&m);
//    printf("n=%d, m=%d\n",n,m);
    int* a=(int*)malloc(sizeof(int)*n);
    int* b=(int*)malloc(sizeof(int)*m);
    int* c=(int*)malloc(sizeof(int)*n);
    int* d=(int*)malloc(sizeof(int)*n);
    for(int i=0;i<n;i++)scanf("%d",a+i);getchar();
    for(int i=0;i<m;i++)scanf("%d",b+i);getchar();
/*
    printf("a={");
    print_ivector(a,n);
    printf("}\n");
    printf("b={");
    print_ivector(b,m);
    printf("}\n");
*/

    for(int i=0;i<n;i++){
        *(c+i)=-1;
        *(d+i)=-1;
    }
    for(int i=0;i<n;i++){
        if (*(a+i)>max){
            max=*(a+i);
            max_idx=i;
            for(int i=0;i<=max_count;i++){
                *(c+i)=-1;
            }
            *c=i;
            max_count=1;
        }else if (*(a+i)==max){
            *(c+max_count)=i;
            max_count++;
        }
        if(*(a+i)<min){
            min=*(a+i);
            min_idx=i;
            for(int i=0;i<=min_count;i++){
                *(d+i)=-1;
            }
            *d=i;
            min_count=1;
        }else if (*(a+i)==min){
            *(d+min_count)=i;
            min_count++;
        }
    }
//    printf("max : %d, %d個\n",max,max_count+1);
//    printf("min : %d, %d個\n",min,min_count+1);
    for(int i=0;i<m;i++){
        for(int j=0;j<max_count;j++){
            if(*(b+i)==*(c+j)+1){
                printf("Yes\n");
                exit(0);
            }
        }
    }
    printf("No\n");
    return 0;
}
void print_ivector(int* a,int n){
    /*
    for(int i=0;i<n;i++){
        printf("%d\n",*(a+i));
    }
    */
    for(int i=0;i<n-1;i++){
            printf("%d ",*(a+i));
        }
    printf("%d\n",*(a+n-1));
}
    

最大値と最小値のインデックスを保持する配列を作るところはRubyでやった場合と同じだが、このように最初に長さが分からない配列を作るのがCではやりにくい。最大値または最小値の個数は最大でaの要素数に等しいので、今回は、aの同じ長さの配列を作り、それに現在まで見つかったインデックスを入れていき、最大値が更新された時にはそれを初期値の-1に戻すというやりかたでやった。


最大値・最小値のインデックス

ABC275-A

問題概要

整数を要素とする配列が与えらたる。その最大値のインデックスを求めよ。

解答

最大値・最小値を求めるのは基本中の基本だが、その際にインデックスも更新していけばよい。

        n=gets.to_i
a=gets.chomp.split(" ").map(&:to_i)
idx=0
max=0
n.times do |i|
    if a[i]>max
        max=a[i]
        idx=i
    end
end
puts idx+1
    

これは本番中の提出コード。

Cで書いたのが以下。問題では最大値のインデックスだけ求めればよいが、今後使うことも考えて最小値のインデックスも一緒に求めるコードを書いた。

#include<stdio.h>
#include<stdlib.h>
#define MAX 2e9+2
int main(void){
    int n,min_idx,max_idx,min=MAX,max=0;
    scanf("%d\n",&n);
    int* a=(int*)malloc(sizeof(int)*n);
    for(int i=0;i<n;i++){
        scanf("%d",a+i);
        if (*(a+i)>max){
            max=*(a+i);
            max_idx=i;
        }else if(*(a+i)<min){
            min=*(a+i);
            min_idx=i;
        }
    }
    printf("%d\n",max_idx+1);
//    printf("%d\n",min_idx+1);
    return 0;
}
    

より汎用性を求めるなら、インデックスと最大値・最小値を配列で返す関数を作っておいてもいい。


最大値または最小値が複数あって、そのインデックスをすべて答える必要がある場合

ans=[]
n.times do |i|
    if cc[i]>max
        ans.clear
        max=cc[i]
        ans.push(i)
    elsif cc[i]==max
        ans.push(i)
    end
end
ans.size.times do |i|
    puts ans[i]+1
end
    

最大値が更新されたときに、インデックスが格納された配列をクリアしてそのインデックスを入れる、もし最大値と同じ値がでてきたら、そのインデックスを答えの配列に追加する、としていけばよい。


後ろから数えたインデックス

ABC276-A

問題概要

英小文字からなる文字列sが与えられる。sに文字aが現れるならば最後に現れるのが何文字目かを出力し、現れないならば−1を出力

文字列を分割して配列にし、末尾から順に見ていけばいい。

解答

a=gets.chomp.split("").to_a
s=a.size
s.downto(0).each do |i|
    if a[i]=="a"
        puts i+1
        exit
    end
end
puts -1
    

これが本番で提出したコード

Cでやったのが以下。

#include<stdio.h>
#include<stdlib.h>
int main(void){
    int n,m,ans=0;  
    scanf("%d %d\n",&n,&m);
    int* a=(int*)malloc(sizeof(int)*n);
    for(int i=0;i<n;i++){
        scanf("%d",a+i);
        if(*(a+i)==m){
            ans=i+1;
            break;
        }
    }
    printf("%d\n",ans);
    return 0;
}
    

要素の出現位置(前から数えたインデックス)を調べる

これは、最も基本的な問題。

ABC277-A

問題概要

n個の数字からなる配列が与えられる、mが何番目に出現するか求めよ。

n,k=gets.chomp.split(&quot; &quot;).map(&amp;:to_i)
a=gets.chomp.split(&quot; &quot;).map(&amp;:to_i)
idx=0
n.times do |i|
    if a[i]==k
        idx=i
        break
    end
end
puts idx+1
    

これが本番で提出したコード。

Cでやったのが以下。

#include<stdio.h>
#include<stdlib.h>
int main(void){
    int n,m,ans=0;  
    scanf("%d %d\n",&n,&m);
    int* a=(int*)malloc(sizeof(int)*n);
    for(int i=0;i<n;i++){
        scanf("%d",a+i);
        if(*(a+i)==m){
            ans=i+1;
            break;
        }
    }
    printf("%d\n",ans);
    return 0;
}
    

ABC297-A

問題概要

配列の隣接する要素の差が初めて一定値以下になる箇所を出力せよ。

解答

n,d=gets.chomp.split(" ").map(&:to_i)
t=gets.chomp.split(" ").map(&:to_i)
(1...n).each do |i|
    if t[i]-t[i-1]<=d
        puts t[i]
        exit
    end
end
puts -1
    

上が本番で提出したコード。以下はCで書いたもの。

void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N 1000000100
int main(void){
    char* s=(char*)malloc(sizeof(char)*N);
    fgets(s,N,stdin);
    int n=readint(s);
    int i=0;
    while(*(s+i)!=32)i++;
    i++;
    int d=readint(s+i);
    free(s);
    //printf("%d %d\n",n,d);
    char* s2=(char*)malloc(sizeof(char)*N);
    fgets(s2,N,stdin);
    int* a=(int*)malloc(sizeof(int)*n);
    input_iarray(s,a,n,N);
    for(int i=1;i<n;i++){
        if((*(a+i)-*(a+i-1))<=d){
            printf("%d\n",*(a+i));
            exit(0);
        }
    }
    printf("%d\n",-1);
    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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        if (*(s+i)<48 || *(s+i)>57){i++;}
        else{
            int bidx=i;
            while(*(s+i)>=48 && *(s+i)<=57){
                i++;
            }
            int t=0;
            for(int j=bidx;j<i;j++){
                t*=10;
                t+=*(s+j)-48;
            }
        *(a+aidx++)=t;
        }
    }
    if (aidx!=n)printf("n!=aidx\n");
}
    

Rubyだと61秒かかっていたものがCでは1秒と、猛烈に速い。

ところで、このコードをCで試すとSegmentation fault (core dumped)というエラーになり、原因が分からずStackoverflowで質問した。その際に、n,dの入力部分は省略したコードを載せたのだが、これは質問のしかたとしてはよくなかったようで、批判を頂いた。結局、原因はn,dの入力用に確保したメモリsでそのまま配列の入った文字列を読み込んだところにあったようで、そこを別にメモリを確保しなおしたら正常に動いた。


ABC299-B

問題概要

整数n,tと二つの整数列が与えられる。配列cにtが一個以上含まれていれば、c[i]==tとなるr[i]のうちで最大のもののインデックス(1-indexed)を、tが配列cに含まれていなければ、c[i]==c[1]となるr[i]のうちで最大のもののインデックス(1-indexed)を出力せよ。

解答

n,t=gets.chomp.split(" ").map(&:to_i)
c=gets.chomp.split(" ").map(&:to_i)
r=gets.chomp.split(" ").map(&:to_i)
cnt=0
idxs1=[]
idxs2=[]
max1=0
max2=0
m1_idx=0
m2_idx=0
n.times do |i|
    if c[i]==t
        cnt+=1
        idxs1.push(i)
        if r[i]>max1
            max1=r[i]
            m1_idx=i
        end
    elsif c[i]==c[0]
        idxs2.push(i)
        if r[i]>max2
            max2=r[i]
            m2_idx=i
        end
    end
end
if cnt>0
    puts m1_idx+1
else
    puts m2_idx+1
end
    

上が本番で提出したコード。以下はCで書いたもの。

typedef struct {
    int c;
    int r;
} player;
void print_player(player *pl, int n);
void print_ivector(int* a,int n);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 20
#define N 100000002
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    fgets(s1,N1,stdin);
    int i=0;
    int n=readint(s1);
    while(*(s1+i)!=32)i++;
    i++;
    int t=readint(s1+i);
    free(s1);
    char* s2=(char*)malloc(sizeof(char)*N);
    fgets(s2,N,stdin);
    char* s3=(char*)malloc(sizeof(char)*N);
    fgets(s3,N,stdin);
    int * c=(int*)malloc(sizeof(int)*n);
    int * r=(int*)malloc(sizeof(int)*n);
    input_iarray(s2,c,n,N);
    input_iarray(s3,r,n,N);
//    print_ivector(c,n);
//    print_ivector(r,n);
    player* pls=(player*)(malloc(sizeof(player)*n));
    for(int i=0;i<n;i++){
        (pls+i)->c=*(c+i);
        (pls+i)->r=*(r+i);
    }
//    print_player(pls,n);
    int max1=-1,max2=pls->r;
    int max1_idx=-1,max2_idx=0;
    int pl1c=pls->c;
    for(int i=0;i<n;i++){
        if ((pls+i)->c == t && (pls+i)->r > max1){
            max1=(pls+i)->r;
            max1_idx=i;
        }
        if ((pls+i)->c == pl1c  && (pls+i)->r > max2){
            max2=(pls+i)->r;
            max2_idx=i;
        }
    }
    if (max1==-1)printf("%d\n",max2_idx+1);
    else printf("%d\n",max1_idx+1);
    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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        if (*(s+i)<48 || *(s+i)>57){i++;}
        else{
            int bidx=i;
            while(*(s+i)>=48 && *(s+i)<=57){
                i++;
            }
            int t=0;
            for(int j=bidx;j<i;j++){
                t*=10;
                t+=*(s+j)-48;
            }
        *(a+aidx++)=t;
        }
    }
    if (aidx!=n)printf("n!=aidx\n");
}
void print_ivector(int* a,int n){
    for(int i=0;i<n-1;i++){
        printf("%d ",*(a+i));
    }
    printf("%d\n",*(a+n-1));
}
void print_player(player *pl, int n){
    for(int i=0;i<n;i++)printf("[%d %d], ",(pl+i)->c,(pl+i)->r);
    printf("\n");
}
    

ABC300-A

問題概要

与えられた整数列のなかに、和がa+bであるものがあるか判定せよ。

解答

n,a,b=gets.chomp.split(" ").map(&:to_i)
c=gets.chomp.split(" ").map(&:to_i)
idx=0
n.times do |i|
    if c[i]==(a+b)
        idx=i
        break
    end
end
puts idx+1
    

上が本番で提出したコード。以下はCで書いたもの。

void input_iarray(char* s,int* a,int n,int imax);
int str_toi(char* s);
#include<stdio.h>
#include<stdlib.h>
#define N1 13
#define N 1000000002
int main(void){
    int n,a,b;
    int i=0;
    char *s=(char*)malloc(sizeof(char)*N1);
    fgets(s,N1,stdin);
//    printf("%s\n",s);
    n=str_toi(s);
    while(*(s+i)!=32)i++;
    i++;
    a=str_toi(s+i);
    while(*(s+i)!=32)i++;
    i++;
    b=str_toi(s+i);
//    printf("%d %d %d\n",n,a,b);
    char *s2=(char*)malloc(sizeof(char)*N);
    fgets(s2,N,stdin);
    int* c=(int*)malloc(sizeof(int)*n);
    input_iarray(s2,c,n,N);
/*    for(int j=0;j<n;j++){
        printf("%d ",*(c+j));
    }
    printf("\n");*/
    for(int j=0;j<n;j++){
        if(*(c+j)==(a+b)){
            printf("%d\n",j+1);
            exit(0);
        }
    }
    printf("No\n");
    return 0;
}
int str_toi(char* s){
    int t=0,i=0,minus_flag=0;
    while((*(s+i)<48 || *(s+i)>57) && *(s+i)!='-' && *(s+i)!='+')i++;
    if (*(s+i)=='-'){
        minus_flag=1; i++;
    }else if (*(s+i)=='+'){
        i++;
    }
    while(*(s+i)>=48 && *(s+i)<=57){
        t*=10;
        t+=*(s+i)-48;i++;
    }
    if (minus_flag)t*=(-1);
    //printf("%s: %d\n",s,t);
    return t;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        if (*(s+i)<48 || *(s+i)>57){i++;}
        else{
            int bidx=i;
            while(*(s+i)>=48 && *(s+i)<=57){
                i++;
            }
            int t=0;
            for(int j=bidx;j<i;j++){
                t*=10;
                t+=*(s+j)-48;
            }
        *(a+aidx++)=t;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
    

連続する整数列の中の抜けている数字を見つける

ABC317-B

問題概要

連続する整数列の中の一つの要素が抜けた数列が与えられる。その抜けている要素を出力せよ。

解答

n=gets.to_i
a=gets.chomp.split(" ").map(&:to_i)
min=a.min
max=a.max
(min..max).each do |it|
    if !a.include?(it)
        puts it
    end
end
    

答えは一意に定まるという制約があるので、与えられた配列の両端に抜けている数字が来ることはない。なので、与えられた配列の最大値と最小値の間に抜けている数がないか探せばよい。

上が本番で提出したコード。以下はCで書いたもの。なお、Cでのコードを書いたときに、毎回ループを回すのは無駄なので、最初に配列をソートしてしまった方がいいと気付いたが、ソートのコードをすぐには書けなかったので、やり方はとりあえずrubyでやったときと同じにした。

void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
#include<stdio.h>
#include<limits.h>
#include<stdlib.h>
#define N1 20
#define N 100000002
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    fgets(s1,N1,stdin);
    int n=readint(s1);
    char* s3=(char*)malloc(sizeof(char)*N);
    fgets(s3,N,stdin);
    int * a=(int*)malloc(sizeof(int)*n);
    input_iarray(s3,a,n,N);
    int max=0,min=INT_MAX;
    for(int i=0;i<n;i++){
        if (max<*(a+i))max=*(a+i);
        if (min>*(a+i))min=*(a+i);
    }
    for(int i=min+1;i<max;i++){
        int flag=0;
        for(int j=0;j<n;j++){
            if (*(a+j)==i)flag=1;
        }
        if (flag==0){
            printf("%d\n",i);
            exit(0);
        }
    }
    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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        if (*(s+i)<48 || *(s+i)>57){i++;}
        else{
            int bidx=i;
            while(*(s+i)>=48 && *(s+i)<=57){
                i++;
            }
            int t=0;
            for(int j=bidx;j<i;j++){
                t*=10;
                t+=*(s+j)-48;
            }
        *(a+aidx++)=t;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
    

二番目に大きい値

ABC329-B

問題概要

整数を要素とする配列の中で、最大値ではない数のうち最大のものを出力せよ。なお、配列の全てが同じ数であることはない。

解答

制約により配列の要素の全てが同じ数である可能性はないので、uniqメソッドで配列の重複要素を削除すれば要素数は必ず2個以上になる。それを(昇順で)ソートして、後ろから2番目のものを出力すればよい。

n=gets.to_i
a=gets.chomp.split(" ").map(&:to_i)
a.uniq!
a.sort!
puts a[a.size-2]
    

上が本番で提出したコード。以下はCで書いたもの。

typedef struct {
    int v;
    struct tn *l;
    struct tn *r;
} tn;
int push_element(tn* f, int* ar);
tn* create_tn(int v);
void insert_tn(int v, tn *node);
int seccond_largest(tn* baum, int prev);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N1 20
#define N 100000002
tn *root=NULL;
int cnt=0, ar_idx=0;
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    fgets(s1,N1,stdin);
    int n=readint(s1);
    char* s3=(char*)malloc(sizeof(char)*N);
    fgets(s3,N,stdin);
    int * a=(int*)malloc(sizeof(int)*n);
    input_iarray(s3,a,n,N);
    for(int i=0;i<n;i++){
        insert_tn(*(a+i),root);
    }
    int * b=(int*)malloc(sizeof(int)*cnt);
    push_element(root,b);
    printf("%d\n",*(b+cnt-2));    
    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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        if (*(s+i)<48 || *(s+i)>57){i++;}
        else{
            int bidx=i;
            while(*(s+i)>=48 && *(s+i)<=57){
                i++;
            }
            int t=0;
            for(int j=bidx;j<i;j++){
                t*=10;
                t+=*(s+j)-48;
            }
        *(a+aidx++)=t;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
tn* create_tn(int v){
    tn* neu;
    neu=(tn*)malloc(sizeof(tn));
    if(neu==NULL){
        printf("Memory allocation failure.");
        exit(0);
    }
    neu->l=NULL;
    neu->r=NULL;
    neu->v=v;cnt++;
    return neu;
}
void insert_tn(int v, tn *node){
    if(node==NULL){
        root=create_tn(v);
        return;
    } else if (v == node->v){
        return;
    } else if (v < node->v){
        if (node->l !=NULL){
            insert_tn(v,node->l);
        } else {
            node->l = create_tn(v);
        }
    } else if (node->v < v){
        if (node->r !=NULL){
            insert_tn(v,node->r);
        } else {
            node->r = create_tn(v);
        }
    }
    return;
}
int push_element(tn* f, int* ar){
    if(f->l!=NULL){
        push_element(f->l,ar);
    }
    *(ar+(ar_idx++))=f->v;
    if(f->r!=NULL){
        push_element(f->r,ar);
    }
} 
    

二分木の実装自体は書籍に載っていたコードを見て書いたので問題なかったが、二番目に大きい値を出力する部分でかなり手間取る。初めは、木から直接に二番目に大きい値が入っているノードを求めるコードを書こうとしたが、うまくいかないので、結局、一旦木の内容を配列に出力し、その最後から二番目の要素を出力することにした。いずれにせよ、こちらの方が汎用性のあるコードではある。

2022年11月24日木曜日

配列の要素の和

配列の和を求める

ABC272-A

問題概要

配列aが与えられたとき、aの要素の総和を求めよ。

解答

解答1

Arrayクラスにsumメソッドが用意されているので、それを使えばよい。

n=gets.to_i
a=gets.chomp.split(" ").map(&:to_i)
puts a.sum
    

上が本番で提出したコード。

解答2

Arrayクラスのinjectメソッドを使う。

n=gets.to_i
a=gets.chomp.split(" ").map(&:to_i)
puts a.inject {|result, item| result + item }
    

自分で、要素を一個ずつ足す処理を書いていってもいい。

解答3

n=gets.to_i
a=gets.chomp.split(" ").map(&:to_i)
sum=0
a.size.times do |i|
    sum+=a[i]
end
puts sum
    

解答4

n=gets.to_i
a=gets.chomp.split(" ").map(&:to_i)
sum=0
a.each do |item|
    sum+=item
end
puts sum
    

実行時間は、

実行時間(ms)
解答169
解答259
解答359
解答458

となった。

Cでやったのは以下。実行時間は 1 ms。

int isum(int* a, int n);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 20
#define N 100000002
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    fgets(s1,N1,stdin);
    int n=readint(s1);
    char* s3=(char*)malloc(sizeof(char)*N);
    fgets(s3,N,stdin);
    int * a=(int*)malloc(sizeof(int)*n);
    input_iarray(s3,a,n,N);
    printf("%d\n",isum(a,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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        if (*(s+i)<48 || *(s+i)>57){i++;}
        else{
            int bidx=i;
            while(*(s+i)>=48 && *(s+i)<=57){
                i++;
            }
            int t=0;
            for(int j=bidx;j<i;j++){
                t*=10;
                t+=*(s+j)-48;
            }
        *(a+aidx++)=t;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
int isum(int* a, int n){
    int sum=0;
    for(int i=0;i<n;i++)sum+= *(a+i);
    return sum;
}
    

配列の一部の和(インデックスを指定)

ABC290-A

問題概要

配列aの、b[1]..b[m]番目の和を求めよ。

解答

n,m=gets.chomp.split(" ").map(&:to_i)
a=gets.chomp.split(" ").map(&:to_i)
b=gets.chomp.split(" ").map(&:to_i)
ans=0
b.each do |it|
    ans+=a[it-1]
end
puts ans
    

上が本番で提出したコード。以下はCで書いたもの。

int selected_isum(int* a, int* idxs,int n,int m);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 20
#define N 100000002
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    fgets(s1,N1,stdin);
    int n=readint(s1);
    int j=0;
    while(*(s1+j)!=32)j++;
    j++;
    int m=readint(s1+j);
//    printf("n=%d m=%d\n",n,m);
    free(s1);
    char* s2=(char*)malloc(sizeof(char)*N);
    fgets(s2,N,stdin);
    int * a=(int*)malloc(sizeof(int)*n);
    input_iarray(s2,a,n,N);
    free(s2);
    char* s3=(char*)malloc(sizeof(char)*N);
    fgets(s3,N,stdin);
    int * b=(int*)malloc(sizeof(int)*m);
    input_iarray(s3,b,m,N);
    free(s3);
    printf("%d\n",selected_isum(a,b,n,m));
    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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        if (*(s+i)<48 || *(s+i)>57){i++;}
        else{
            int bidx=i;
            while(*(s+i)>=48 && *(s+i)<=57){
                i++;
            }
            int t=0;
            for(int j=bidx;j<i;j++){
                t*=10;
                t+=*(s+j)-48;
            }
        *(a+aidx++)=t;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
int selected_isum(int* a, int* idxs,int n,int m){
    int sum=0;
    for(int i=0;i<m;i++){
        int idx=*(idxs+i)-1;
        sum+= *(a+idx);
    }
    return sum;
}
    

群ごとの和

ABC307-A

問題概要

与えられた配列を7個ごとの群に区切り、各群の数の和を順に出力せよ。

解答

nn=gets.to_i
a=gets.chomp.split(" ").map(&:to_i)
n=nn*7
r=n%7
if r>0
    b_size=(n/7+1)
else
    b_size=(n/7)
end
b=Array.new(b_size,0)
(n/7).times do |i|
    7.times do |j|
        b[i]+=a[i*7+j]
    end
end
if r>0
    r.times do |j|
        b[i+1]+=a[(n/7)*7+j]
    end
end
puts b.join(" ")
    

最初、nは与えられる配列の要素数だと思って書き進めたのだが、そうではなく、群の数だった。更に、7で割って余りが生じるかどうかの場合分けも不要であった。

上が本番で提出したコード。以下はCで書いたもの。

void print_ivector(int* a,int n);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 20
#define N 100000002
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    fgets(s1,N1,stdin);
    int n=readint(s1);
    free(s1);
    char* s2=(char*)malloc(sizeof(char)*N);
    fgets(s2,N,stdin);
    int* a=(int*)malloc(sizeof(int)*n*7);
    input_iarray(s2,a,n*7,N);
    free(s2);
    int* weekly=(int*)malloc(sizeof(int)*n);
    for(int i=0;i<n;i++)*(weekly+i)=0;
    int j=0;
    int wn=0;
    while(j<7*n){
        *(weekly+wn)+= *(a+7*wn+j%7);
        if (j%7==6)wn++;
        j++;
    }
    print_ivector(weekly,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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        if (*(s+i)<48 || *(s+i)>57){i++;}
        else{
            int bidx=i;
            while(*(s+i)>=48 && *(s+i)<=57){
                i++;
            }
            int t=0;
            for(int j=bidx;j<i;j++){
                t*=10;
                t+=*(s+j)-48;
            }
        *(a+aidx++)=t;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
void print_ivector(int* a,int n){
    for(int i=0;i<n-1;i++){
        printf("%d ",*(a+i));
    }
    printf("%d\n",*(a+n-1));
}
    

条件を満たす要素の和

ABC328-B

問題概要

整数からなる配列が与えられる。値が一定値以下である要素の和を出力せよ。

解答

n,x=gets.chomp.split(&quot; &quot;).map(&amp;:to_i)
a=gets.chomp.split(&quot; &quot;).map(&amp;:to_i)
ans=0
(0...n).each do |i|
    if a[i]&lt;=x
        ans+=a[i]
    end
end
puts ans
    

上が本番で提出したコード。以下はCで書いたもの。

int if_smaller(int* np,int x);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 20
#define N 100000002
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    fgets(s1,N1,stdin);
    int n=readint(s1);
    int j=0;
    while(*(s1+j)!=32)j++;
    j++;
    int x=readint(s1+j);
//    printf("n=%d m=%d\n",n,m);
    free(s1);
    char* s2=(char*)malloc(sizeof(char)*N);
    fgets(s2,N,stdin);
    int * a=(int*)malloc(sizeof(int)*n);
    input_iarray(s2,a,n,N);
    free(s2);
    int ans=0;
    for(int i=0;i<n;i++)ans+= (if_smaller(a+i,x));
    printf("%d\n",ans);
    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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        if (*(s+i)<48 || *(s+i)>57){i++;}
        else{
            int bidx=i;
            while(*(s+i)>=48 && *(s+i)<=57){
                i++;
            }
            int t=0;
            for(int j=bidx;j<i;j++){
                t*=10;
                t+=*(s+j)-48;
            }
        *(a+aidx++)=t;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
int if_smaller(int* np,int x){
    if (*np<=x)return *np;
    else return 0;
}
    

2022年11月13日日曜日

要素の数を数える問題・正の字カウント・ハッシュ

数を数える

今回は、具体的なABCの問題から入るのではなく、一般的な場合から。

与えられたデータの中に、区別できるものがそれぞれ何個ずつあるかを数える。。

まずは、要素の種類が分かっている場合

例えば、o,xだけとか、"Yes", "No"だけとか、一桁の数字だけとか。

もっと多くて、都道府県とか、アメリカ合衆国の州とかいう場合も考えられる。

こういう時には、あらかじめハッシュテーブルを用意しておいて、ハッシュのキーと同じものが出てきたら、そのキーのの値を一つずつ増やしていけばよい。

設例1

データ oxooxoxxxxoo が標準入力から与えられるとする。この中に、o と x はそれぞれ何個ずつあるか。

解答

h={o:0,x:1}
a=gets.chomp.split("").to_a
a.each do |it|
    h[it.to_sym]+=1
end
puts "o:#{h[:o]}"
puts "x:#{h[:x]}"    
    

実行結果は、

o:3
x:3
    

次に、出現する要素があらかじめわかっていない場合どうするかを見ていく。

あらかじめわかっていない要素の数を数える問題

[a,a,a,b,b,c]のような、特異な要素がそれぞれ複数個ありうる配列が与えられて、その特異な要素ごとにその数を数える、というタイプの問題である。実用度はかなり高い。手作業でやる時には、「順番に見ていって、新しい要素が出てきたらその名前を書いて、その下に正の字の最初の一画を書き、二回以上出現した要素については、既に書いた名前の下の正の字の画数を増やしていく」、ということをするのだが、それをコンピューターでやろうとするとどうするか。

データ

μῆνιν ἄειδε θεὰ Πηληϊάδεω Ἀχιλῆος
οὐλομένην, ἣ μυρί᾽ Ἀχαιοῖς ἄλγε᾽ ἔθηκε,
πολλὰς δ᾽ ἰφθίμους ψυχὰς Ἄϊδι προΐαψεν
ἡρώων, αὐτοὺς δὲ ἑλώρια τεῦχε κύνεσσιν
5οἰωνοῖσί τε πᾶσι, Διὸς δ᾽ ἐτελείετο βουλή,
ἐξ οὗ δὴ τὰ πρῶτα διαστήτην ἐρίσαντε
Ἀτρεΐδης τε ἄναξ ἀνδρῶν καὶ δῖος Ἀχιλλεύς.
τίς τ᾽ ἄρ σφωε θεῶν ἔριδι ξυνέηκε μάχεσθαι;
の中に出てくる文字を文字ごとに集計せよ。(アクセント記号、気息記号が異なれば別の文字として扱う。)

解答

一つの案は、配列を使うやり方で、

s=<<'EOS'
μῆνιν ἄειδε θεὰ Πηληϊάδεω Ἀχιλῆος
οὐλομένην, ἣ μυρί᾽ Ἀχαιοῖς ἄλγε᾽ ἔθηκε,
πολλὰς δ᾽ ἰφθίμους ψυχὰς Ἄϊδι προΐαψεν
ἡρώων, αὐτοὺς δὲ ἑλώρια τεῦχε κύνεσσιν
5οἰωνοῖσί τε πᾶσι, Διὸς δ᾽ ἐτελείετο βουλή,
ἐξ οὗ δὴ τὰ πρῶτα διαστήτην ἐρίσαντε
Ἀτρεΐδης τε ἄναξ ἀνδρῶν καὶ δῖος Ἀχιλλεύς.
τίς τ᾽ ἄρ σφωε θεῶν ἔριδι ξυνέηκε μάχεσθαι;
EOS
list=[]
number=[]
(0...s.size).each do |i|
    if s[i]=="\n" || s[i]==" "
        next
    elsif list.include?(s[i])
        number[list.index(s[i])]+=1
    else
        list.push(s[i])
        number.push(1)
    end
end
list.size.times do |i|
    puts "#{list[i]} : #{number[i]}"
end
    

実行結果は、

μ : 5
ῆ : 2
ν : 16
ι : 14
ἄ : 4
ε : 22
δ : 12
θ : 5
ὰ : 4
Π : 1
η : 7
λ : 11
ϊ : 2
ά : 2
ω : 4
Ἀ : 4
χ : 6
ο : 14
ς : 11
ὐ : 2
έ : 2
, : 5
ἣ : 1
υ : 5
ρ : 10
ί : 6
᾽ : 5
α : 10
ῖ : 3
γ : 1
ἔ : 2
κ : 4
π : 4
ἰ : 2
φ : 2
ψ : 2
Ἄ : 1
ΐ : 2
ἡ : 1
ώ : 2
τ : 14
ὺ : 1
ὲ : 1
ἑ : 1
ῦ : 1
ύ : 2
σ : 8
5 : 1
ᾶ : 1
Δ : 1
ὸ : 1
ἐ : 3
β : 1
ή : 2
ξ : 3
ὗ : 1
ὴ : 1
ῶ : 3
ἀ : 1
ὶ : 1
. : 1
; : 1
のようになった。

もう一つのやり方として、rubyにはハッシュという便利なクラスが用意されているので、それを使うもの。実際に問題を解く際にはたいていこちらでやっているので、これについてはそちらの実例を参照。


数を数える

ABC240-B

問題概要

長さ N の正整数列 aには何種類の整数が現れるか。

解答

n=gets.to_i
a=gets.split.map(&:to_i)
b=[]
c=[]
n.times do |i|
    if b.include?(a[i])
        c[b.index(a[i])]+=1
    else
        b.push(a[i])
        c[b.index(a[i])]=1
    end
end
puts b.size
    

上が本番で提出したコード。Cで最初にやったのが以下。(REになった。)

void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 20
#define N 2e9
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    fgets(s1,N1,stdin);
    int n=readint(s1);
    free(s1);
    char* s2=(char*)malloc(sizeof(char)*N);
    fgets(s2,N,stdin);
    int * a=(int*)malloc(sizeof(int)*n);
    input_iarray(s2,a,n,N);
    free(s2);
    int max=0;
    for(int i=0;i<n;i++){
        if (*(a+i)>max)max=*(a+i);
    }
    int * nums=(int*)malloc(sizeof(int)*(max+1));
    for(int i=0;i<=max;i++)*(nums+i)=0;
    for(int i=0;i<n;i++){
        int num=*(a+i);
        *(nums+num)+=1;
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        if (*(nums+i))ans++;
    }
    printf("%d\n",ans);
    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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        if (*(s+i)<48 || *(s+i)>57){i++;}
        else{
            int bidx=i;
            while(*(s+i)>=48 && *(s+i)<=57){
                i++;
            }
            int t=0;
            for(int j=bidx;j<i;j++){
                t*=10;
                t+=*(s+j)-48;
            }
        *(a+aidx++)=t;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
    

サンプル3件ではACになるが、他のテストケースでは全てREになった。

Paiza.ioにテストケースのデータを入れてやってみると、こちらでもエラーになる。一旦コード全体をコメントアウトし、少しずつコメントアウトを外しながらエラーが起る場所を探してみると、問題の箇所は25行目(*(nums+num)+=1;)のようだ。

だが、メモリは十分に確保しているはずなのに、どうしてエラーになるのか分からず、StackOverflowで質問。

原因は、メモリ制限がPaiza.ioで512MB, AtCoderでも1024MBなのに対し、制約上のa[i]の最大値10^9個のint型メモリを確保しようとすると4000MBになってしまうのが原因のようだ。それで、mallocがNULLを返すが、そこにそのままデータを入れようとしてランタイムエラーになったらしい。(attakeiさん、actorbugさん、コメント・回答有難うございました。)

それで、結局二分木を使って出現した数のリストを作ることに。と言っても、少し前に作ったABC329-Bのコードがほぼそのまま使えたので、新しく書いたところはほとんどない。それが以下。

typedef struct {
    int v;
    struct tn *l;
    struct tn *r;
} tn;
int push_element(tn* f, int* ar);
tn* create_tn(int v);
void insert_tn(int v, tn *node);
int seccond_largest(tn* baum, int prev);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N1 20
#define N 100000002
tn *root=NULL;
int cnt=0, ar_idx=0;
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    fgets(s1,N1,stdin);
    int n=readint(s1);
    char* s3=(char*)malloc(sizeof(char)*N);
    fgets(s3,N,stdin);
    int * a=(int*)malloc(sizeof(int)*n);
    input_iarray(s3,a,n,N);
    for(int i=0;i<n;i++){
        insert_tn(*(a+i),root);
    }
    int * b=(int*)malloc(sizeof(int)*cnt);
    push_element(root,b);
    printf("%d\n",cnt);    
    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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        if (*(s+i)<48 || *(s+i)>57){i++;}
        else{
            int bidx=i;
            while(*(s+i)>=48 && *(s+i)<=57){
                i++;
            }
            int t=0;
            for(int j=bidx;j<i;j++){
                t*=10;
                t+=*(s+j)-48;
            }
        *(a+aidx++)=t;
        }
    }
    if (aidx!=n)printf("n!=aidx aidx=%d\n",aidx);
}
tn* create_tn(int v){
    tn* neu;
    neu=(tn*)malloc(sizeof(tn));
    if(neu==NULL){
        printf("Memory allocation failure.");
        exit(0);
    }
    neu->l=NULL;
    neu->r=NULL;
    neu->v=v;cnt++;
    return neu;
}
void insert_tn(int v, tn *node){
    if(node==NULL){
        root=create_tn(v);
        return;
    } else if (v == node->v){
        return;
    } else if (v < node->v){
        if (node->l !=NULL){
            insert_tn(v,node->l);
        } else {
            node->l = create_tn(v);
        }
    } else if (node->v < v){
        if (node->r !=NULL){
            insert_tn(v,node->r);
        } else {
            node->r = create_tn(v);
        }
    }
    return;
}
int push_element(tn* f, int* ar){
    if(f->l!=NULL){
        push_element(f->l,ar);
    }
    *(ar+(ar_idx++))=f->v;
    if(f->r!=NULL){
        push_element(f->r,ar);
    }
} 
    

ABC263-A

問題概要

フルハウスかどうか判定(正の字カウント)

ABC263-A

問題概要

5つの数がが与えられる。それらが、それぞれ3個と2個の二種類の数字からなるかどうか判定せよ。

解答

本番では別のやり方でやったのだが、これは正の字カウントでやった方が素直だし早い。

a=gets.chomp.split(" ").map(&:to_i)
h=Hash.new
a.each do |item|
    if h.has_key?(item)
        h[item]+=1
    else
        h[item]=1
    end
end
ss=h.size
if ss!=2
    puts "No"
    exit
end
hv=h.values
if hv[0]==2 && hv[1]==3 || hv[0]==3 && hv[1]==2
    puts "Yes"
else
    puts "No"
end
    

Cで書いたのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 22
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=(int*)malloc(sizeof(int)*5);
    if (a==NULL){
        printf("memory allocation to a failed.\n");
        exit(1);
    }
    int i=0,j=0;
    for(;i<4;i++){
        *(a+i)=readint(s1+j);
        while(*(s1+j)!=32)j++;
        j++;
    }
    *(a+i)=readint(s1+j);
    int* r=(int*)malloc(sizeof(int)*14);
    if (a==NULL){
        printf("memory allocation to r failed.\n");
        exit(1);
    }
    for(i=0;i<=13;i++)*(r+i)=0;
    for(i=0;i<5;i++){
        int t=*(a+i);
        *(r+t)+=1;
    }
    int cnt=0;
    for(i=1;i<=13;i++){
        if(*(r+i))cnt++;
    }
//    printf("%d\n",cnt);
    if (cnt!=2){
        printf("No\n");
        exit(0);
    }else{
        int n1=0,n2=0;
        for(i=1;i<=13;i++){
            if(*(r+i)){
                if (n1==0)n1=*(r+i);
                else n2=*(r+i);
            }
        }
//        printf("n1=%d, n2=%d\n",n1,n2);
        if ((n1==3 && n2==2) || n2==3 && n1==2){
            printf("Yes\n");
            exit(0);
        }else{
            printf("No\n");
            exit(0);
        }
    }
    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;
}
    

昨日やったABC268-Aのコードがかなり流用できたので、今回はmallocのNULLチェックも入れてみた。


ABC268-A

問題概要

5個の数字が与えられる。この中に数字が何種類あるか答えよ。

解答

a=gets.chomp.split(" ").map(&:to_i)
h=Hash.new
a.each do |item|
    if h.has_key?(item)
        h[item]+=1
    else
        h[item]=1
    end
end
puts h.size
    

空ハッシュを作っておき、配列の要素をはじめから順番に見ていって、もしハッシュのキーにその要素が既にあれば、その値を一増やし、もしなければ新しいキーを追加し、その値を1にしている。問題の答えは、最終的なハッシュのサイズになる。

もっとも、この問題の場合、各キーの個数は必要なく、ユニークな要素が何種類あるか分かればそれでいいから、

a=gets.chomp.split(" ").map(&:to_i)
au=a.uniq!
puts au.size
    

としてもよい。

Cで書いたのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 22
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    fgets(s1,N1,stdin);
    int* a=(int*)malloc(sizeof(int)*5);
    int i=0,j=0;
    for(;i<4;i++){
        *(a+i)=readint(s1+j);
        while(*(s1+j)!=32)j++;
        j++;
    }
    *(a+i)=readint(s1+j);
    int* r=(int*)malloc(sizeof(int)*101);
    for(i=0;i<=100;i++)*(r+i)=0;
    for(i=0;i<5;i++){
        int t=*(a+i);
        *(r+t)+=1;
    }
    int ans=0;
    for(i=0;i<=100;i++){
        if(*(r+i))ans++;
    }
    printf("%d\n",ans);
    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;
}
    

数を数える

ABC287-A

問題概要

ForまたはAgainstからなる文字列がn個与えられる。Forが過半数より多いか否か判定せよ。

解答

n=gets.to_i
count=0
n.times do |i|
    s=gets.chomp
    if s=="For"
        count+=1
    end
end
if count>n/2
    puts "Yes"
else
    puts "No"
end
        

上が本番で提出したコード。Cで書いたのが以下。

int scmp(char *s, char *t);
int is_nonletter(char *c);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 20
#define N2 1000
#define N 100000002
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    fgets(s1,N1,stdin);
    int n=readint(s1);
    free(s1);
    char* s=(char*)malloc(sizeof(char)*N2);
    for(int i=0;i<n;i++){
        fgets(s+10*i,10,stdin);
    }
/*    for(int i=0;i<n;i++){
        printf("%s\n",s+10*i);
    }*/
    const char* ar[2]={"For","Against"};
//    printf("%s\n",ar[0]);
    int yes=0,no=0;
    for(int i=0;i<n;i++){
        if(scmp(s+10*i,ar[0])==0)yes++;
        else if(scmp(s+10*i,ar[1])==0)no++;
    }
    if (yes>no){
        printf("Yes");
    }else{
        printf("No");
    }
    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;
}
int scmp(char *s, char *t){
    int i=0;
    //printf("s=%s,t=%s\n",s,t); 
    int flag=4;
    while(1){
        if (is_nonletter(s+i) && is_nonletter(t+i))flag=0;
        else if (is_nonletter(s+i) && !is_nonletter(t+i))flag=-2;
        else if (!is_nonletter(s+i) && is_nonletter(t+i))flag=2;
        else if(*(s+i)>*(t+i))flag=1;
        else if(*(s+i)<*(t+i))flag=-1;
        else if(*(s+i)==*(t+i))i++;
        else flag=-2;
        if (flag!=4)return flag;
    }
    return flag;
}
int is_nonletter(char *c){
    if (*c<=32 || *c==127)return 1;
    else return  0;
}
        

条件に一致するものの数を数える

ABC287-B

問題概要

数字文字列の配列s(文字数6),t(文字数3)がある。配列sに含まれる文字列のうち、その4~6文字目が配列tに含まれる文字列と一致するものがいくつあるか求めよ。

解答

n,m=gets.chomp.split(" ").map(&:to_i)
s=[]
t=[]
count=0
(0...n).each do |i|
    s[i]=gets.chomp
end
(0...m).each do |i|
    t[i]=gets.chomp
end
(0...n).each do |i|
    (0...m).each do |j|
        if s[i].slice(3,3)==t[j]
            count+=1
            break
        end
    end
end
puts count
    

上が本番で提出したコード。Cで書いたのが以下だが、まずはWAになったコード。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 20
#define NS 8000
#define NT 5000
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    fgets(s1,N1,stdin);
    int n=readint(s1);
    int j=0;
    while(*(s1+j)!=32)j++;
    j++;
    int m=readint(s1+j);
//    printf("n=%d m=%d\n",n,m);
    free(s1);
    char* s2=(char*)malloc(sizeof(char)*NS);
    char* s3=(char*)malloc(sizeof(char)*NT);
    for(int i=0;i<n;i++){
        fgets(s2+i*6,8,stdin);
    }
    for(int i=0;i<m;i++){
        fgets(s3+i*3,5,stdin);
    }
    int ans=0;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            int flag=1;
            for(int k=0;k<3;k++){
                if (*(s2+i*6+3+k)!=*(s3+j*3+k)){
                    flag=0;
                    break;
                }
                if (flag==0)break;
            }
            if (flag==1)ans++;
        }
    }
    printf("%d\n",ans);
    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;
}
    

WAになるテストケースを見てみると、tの要素に重複するものがある。つまり、この問題で問われているのは、「Sの要素のうち、tの要素のいずれかと一致するものがいくつあるか。」なので、s[i]がtの要素のうち2つ以上に一致しても、それは一回とか添えなければいけないのである。

すると、まずはtをユニークな配列に作り直さなければならないのか、これをCでやるとなると意外と面倒だなど思いつつ、まずはrubyで書いたときはどうしたか見てみることになる。すると、何か特別な処理をしているようには見えない。これは一体どういうことだ、としばらく悩んだ。(rubyでのコードを書いたのは一年近く前のことなので、その特に何を考えていたかなど覚えていない。)

しばらくして、ようやく、rubyで書いたコードでは、一致するものが見つかった時点でtの要素を回すループを抜けていることに気づく。こうすればよかったのが。意外と単純な解法があった。(なお、rubyではuniqという便利なメソッドがあるので、tを重複要素の無いものにすることも簡単にできる。)それでやり直してACになったのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 20
#define NS 8000
#define NT 5000
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    fgets(s1,N1,stdin);
    int n=readint(s1);
    int j=0;
    while(*(s1+j)!=32)j++;
    j++;
    int m=readint(s1+j);
//    printf("n=%d m=%d\n",n,m);
    free(s1);
    char* s2=(char*)malloc(sizeof(char)*NS);
    char* s3=(char*)malloc(sizeof(char)*NT);
    for(int i=0;i<n;i++){
        fgets(s2+i*6,8,stdin);
    }
    for(int i=0;i<m;i++){
        fgets(s3+i*3,5,stdin);
    }
    int ans=0;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            int flag=1;
            for(int k=0;k<3;k++){
                if (*(s2+i*6+3+k)!=*(s3+j*3+k)){
                    flag=0;
                    break;
                }
                if (flag==0)break;
            }
            if (flag==1){
                ans++;
                break;
            }
        }
    }
    printf("%d\n",ans);
    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;
}
    

列ごとに特定の値の要素を数える

ABC274-B

問題概要

要素が#または.である行列が与えられる。各列の#の数を順に出力せよ

解答

n,m=gets.chomp.split(" ").map(&:to_i)
a=[]
ans=Array.new(m,0)
n.times do |i|
    a[i]=gets.chomp.split("").to_a
    m.times do |j|
        if a[i][j]=="#"
            ans[j]+=1
        end
    end
end
puts ans.join(" ")
    

上が本番で提出したコード

他のやり方としては、転置行列を作って、各行の要素数を数えるというものも考えられる。そうすると、Arrayクラスのcountメソッドが使える。

n,m=gets.chomp.split(" ").map(&:to_i)
a=[]
b=Array.new(m){Array.new(n,"")}
n.times do |i|
    a[i]=gets.chomp.split("").to_a
    m.times do |j|
        b[j][i]=a[i][j]
    end
end
m.times do |i|
    print b[i].count("#")
    if i!=m-1
        print(" ")
    end
end
puts ""
    

ただ、最初のコードの実行時間が285msなのに対し、下のコード475msと、実行時間が約1.7倍に増えている。そのうえメモリ使用量も4%くらい増えているから、本番で敢えてこれを使うメリットはないだろう。

Cでやったのが以下。

void print_ivector(int* a,int n);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N 1e6
int main(void){
    char* s=(char*)malloc(sizeof(char)*N);
    fgets(s,N,stdin);
    int h=readint(s);
    int i=0;
    while(*(s+i)!=32)i++;
    i++;
    int w=readint(s+i);
    free(s);
    //printf("%d %d\n",h,w);
    char* s2=(char*)malloc(sizeof(char)*N+2);
    for(int j=0;j<h;j++){
        char* t=(char*)malloc(sizeof(char)*(w+2));
        fgets(t,w+2,stdin);
        for(int k=0;k<w;k++)*(s2+j*w+k)=*(t+k);
    }
    /*
    for(int j=0;j<h;j++){
        for(int k=0;k<w;k++){
            printf("%c",*(s2+j*w+k));
        }
        printf("\n");
    }
    */
    int* a=(int*)malloc(sizeof(int)*w);
    for(int j=0;j<w;j++){
        int temp=0;
        for(int k=0;k<h;k++){
            if (*(s2+k*w+j)==35)temp++;
        }
        *(a+j)=temp;
    }
    print_ivector(a,w);
    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;
}
void print_ivector(int* a,int n){
    for(int i=0;i<n-1;i++){
        printf("%d ",*(a+i));
    }
    printf("%d\n",*(a+n-1));
    }
    

文字列中の文字の出現回数を数える

ABC279-A

問題概要

vとwからなる文字列が与えられる。下に尖った個所が何カ所あるかを求めよ。

解答

s=gets.chomp.split("").to_a
ans=0
s.size.times do |i|
    if s[i]=="v"
        ans+=1
    elsif s[i]=="w"
        ans+=2
    end
end
puts ans
    

上が本番の提出コード。一文字ずつ見ていって、vが出てきたら1、wが出てきたら2を答えに加算している。

Cでやったのが以下。

#include<stdio.h>
#include<stdlib.h>
#define N 102
int main(void){
    int ans=0;  
    char* s=(char*)malloc(sizeof(char)*N);
    fgets(s,N,stdin);
    for(int n=0;n<N;n++){
        if (*(s+n)==118){
            ans+=1;
        }
        if (*(s+n)==119){
            ans+=2;
        }
    }
    printf("%d\n",ans);
    return 0;
}

行列中の特定の要素の数を数える。

ABC280-A

問題概要

.または#を要素とする行列が与えられる。#が何個あるか出力せよ。

解答

h,w=gets.chomp.split(" ").map(&:to_i)
ans=0
h.times do |i|
    t=gets.chomp.split("").to_a
    c=0
    w.times do |j|
        if t[j]=="#"
            c+=1
        end
    end
    ans+=c
end
puts ans
    

上が本番の提出コード。Cでやったのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N 7
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N);
    fgets(s1,N,stdin);
    int i=0;
    int h=readint(s1);
    while(*(s1+i)!=32)i++;
    i++;
    int w=readint(s1+i);
    free(s1);
//    printf("%d %d\n",h,w);
    int ans=0;
    for(i=0;i<h;i++){
        char* s2=(char*)malloc(sizeof(char)*(w+2));
        fgets(s2,(w+2),stdin);
        for(int j=0;j<w;j++){
//            printf("%c",*(s2+j));
            if(*(s2+j)==35)ans++;
        }
//        printf("\n");
    }
    printf("%d\n",ans);
    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;
}
    

ABC295-C

問題概要

数字を要素とする配列が与えられる。同じ数字のペアは何個作れるか。

解答

「正の字カウント」を使えばよい。

n=gets.to_i
a=gets.chomp.split(" ").map(&:to_i)
h=Hash.new
a.each do |item|
    if h.has_key?(item)
        h[item]+=1
    else
        h[item]=1
    end
end
count=0
h.each_value do |v|
    count+=v/2
end
puts count
    

上が本番の提出コードCでやったのが以下。まずは、以下のコードでTLEになった。

typedef struct {
    int key;
    int v;
    struct tn *l;
    struct tn *r;
} tn;
void print_ivector(int* a,int n);
int tree_to_pa(tn* f, int* a1, int* a2);
tn* create_tn(int key);
void insert_tn(int key, tn *node);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 8
#define N 1e7
tn *root=NULL;
int cnt=0,ar_idx=0;
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 n=readint(s1);
    free(s1);
    char* s=(char*)malloc(sizeof(char)*N);
    if (s==NULL){
        printf("memory allocation to s failed.\n");
        exit(1);
    }
    fgets(s,N,stdin);
    int* a=(int*)malloc(sizeof(int)*n);
    if (a==NULL){
        printf("memory allocation to a failed.\n");
        exit(1);
    }
    int i=0,j=0;
    for(;i<n-1;i++){
        *(a+i)=readint(s+j);
        while(*(s+j)!=32)j++;
        j++;
    }
    *(a+i)=readint(s+j);
    free(s);
//    print_ivector(a,n);
    for(int i=0;i<n;i++){
        insert_tn(*(a+i),root);
    }
    free(a);
    int *a1=(int*)malloc(sizeof(int)*cnt);
    if (a1==NULL){
        printf("memory allocation to a1 failed.\n");
        exit(1);
    }
    int *a2=(int*)malloc(sizeof(int)*cnt);
    if (a2==NULL){
        printf("memory allocation to a2 failed.\n");
        exit(1);
    }
    tree_to_pa(root,a1,a2);
    int ans=0;
    for(i=0;i<cnt;i++){
        ans+= *(a2+i)/2;
    }
//    print_ivector(a1,cnt);
//    print_ivector(a2,cnt);
    printf("%d\n",ans);
    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;
}
tn* create_tn(int key){
    tn* neu;
    neu=(tn*)malloc(sizeof(tn));
    if(neu==NULL){
        printf("Memory allocation failed.");
        exit(1);
    }
    neu->l=NULL;
    neu->r=NULL;
    neu->key=key;cnt++;
    neu->v=1;
    return neu;
}
void insert_tn(int key, tn *node){
    if(node==NULL){
        root=create_tn(key);
        return;
    } else if (key == node->key){
        (node->v)+=1;
        return;
    } else if (key < node->key){
        if (node->l !=NULL){
            insert_tn(key,node->l);
        } else {
            node->l = create_tn(key);
        }
    } else if (node->key < key){
        if (node->r !=NULL){
            insert_tn(key,node->r);
        } else {
            node->r = create_tn(key);
        }
    }
    return;
}
int tree_to_pa(tn* f, int* a1, int* a2){
    if(f->l!=NULL){
        tree_to_pa(f->l,a1,a2);
    }
    *(a1+(ar_idx))=f->key;
    *(a2+(ar_idx))=f->v;ar_idx++;
    if(f->r!=NULL){
        tree_to_pa(f->r,a1,a2);
    }
} 
void print_ivector(int* a,int n){
    for(int i=0;i<n-1;i++){
        printf("%d ",*(a+i));
    }
    printf("%d\n",*(a+n-1));
}
    

少し前にABC329-Bで二分木を使うコードを書いたので、それを少し修正して、二分木の葉にキーと値の二つを持たせ、数字をキーとするハッシュとして使えるようにした。

AtCoderでの実行結果の詳細を見ると、sortedという名の付いた最後4件のデータでTLEになっている。どうやら、二分木にソート済みのデータを入れると枝が一方向に伸び、探索にO(n)の時間がかかるというあの現象が起きているようだ。


決まった要素の出現回数、+同数の場合の処理

ABC301-A

問題概要

AとTからなる文字列が与えられる。AとTのどちらが多いか。ただし、同数の場合、先にその数に達した方を答えよ。

解答

出現する要素の種類が分かっているので、その要素をキーとするハッシュの値を加算していけばよい。同数の場合については、「文字列の最後の要素でない方」を答えればよい。

n=gets.to_i
s=gets.chomp
count=Hash.new
count["A"]=0
count["T"]=0
n.times do |i|
    count[s[i]]+=1
end
if count["A"]>count["T"]
    puts "A"
elsif count["T"]>count["A"]
    puts "T"
else
    if s[-1]=="T"
        puts "A"
    else
        puts "T"
    end
end
    

上が本番で提出したコード。Cで書いたのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 102
int main(void){
    int n,na=0,nt=0;
    char *sn=(char*)malloc(sizeof(char)*5);
    fgets(sn,5,stdin);
    n=readint(sn);
//    printf("%d\n",n);
    char *s=(char*)malloc(sizeof(char)*N1);
    fgets(s,N1,stdin);
//    printf("%s\n",s);
    for(int i=0;i<n;i++){
        if (*(s+i)==65)na++;
        else if (*(s+i)==84)nt++;
    }
    if (na>nt)printf("A\n");
    else if (na<nt)printf("T\n");
    else if (na==nt){
        if (*(s+n-1)==65)printf("T\n");
        else printf("A\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;
}
    

ABC308-B

問題概要

文字列を要素とする配列が与えられる。その各要素は品目名であり、品目名と価格を対応させた表が与えられる。ただし価格表に載っていない品目もあり、その価格はデフォルト値となる。価格の合計を出力せよ。

解答

n,m=gets.chomp.split(" ").map(&:to_i)
c=gets.chomp.split(" ")
d=gets.chomp.split(" ")
p=gets.chomp.split(" ").map(&:to_i)
ans=0
n.times do |i|
    if !d.include?(c[i])
        k=p[0]
    else
        k=p[d.find_index(c[i])+1]
    end
    ans+=k
end
puts ans
    

Cでまず書いたのが以下。このコードは、sample1だけWAになった。

typedef struct {
    int id;
    char key[21];
    int v;
    struct tn *l;
    struct tn *r;
} tn;
void print_tn(tn* tns, int n);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
void print_ivector(int* a,int n);
int tree_to_pa(tn* f, int* a1, int* a2);
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N1 9
#define N2 2102
#define N 100000002
tn* create_tn(int key);
void insert_tn(int key, tn *node);
tn *root=NULL;
int cnt=0,ar_idx=0;
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    if(s1==NULL){
        printf("Memory allocation to s1 failed.");
        exit(1);
    }
    fgets(s1,N1,stdin);
    int i=0;
    int n=readint(s1);
    while(*(s1+i)!=32)i++;
    i++;
    int m=readint(s1+i);
//    printf("n=%d m=%d\n",n,m);
    free(s1);
    char* s2=(char*)malloc(sizeof(char)*(21*n+1));
    if(s2==NULL){
        printf("Memory allocation to s2 failed.");
        exit(1);
    }
    fgets(s2,(21*n+1),stdin);
//    printf("s2=%s\n",s2);
    char* s3=(char*)malloc(sizeof(char)*(21*m+1));
    if(s3==NULL){
        printf("Memory allocation to s3 failed.");
        exit(1);
    }
    fgets(s3,(21*m+1),stdin);
//    printf("s3=%s\n",s3);
    char* s4=(char*)malloc(sizeof(char)*(6*m+1));
    if(s4==NULL){
        printf("Memory allocation to s4 failed.");
        exit(1);
    }
    fgets(s4,6*(m+1)+1,stdin);
//    printf("s4=%s\n",s4);
    int* p=(int*)malloc(sizeof(int)*(m+1));
    if (p==NULL){
        printf("memory allocation to p failed.\n");
        exit(1);
    }
    input_iarray(s4,p,m+1,6*m+1);
//    print_ivector(p,m+1);
    free(s4);
    tn* arr=(tn*)malloc(sizeof(tn)*m);
    if (arr==NULL){
        printf("memory allocation to arr failed.\n");
        exit(1);
    }
    int s3_idx=0;
    i=0;
    for(;i<m;i++){
        char* temp=(char*)malloc(sizeof(char)*22);
        if (temp==NULL){
            printf("memory allocation to temp(%d) failed.\n",i);
            exit(1);
        }
        int k=0;
//        printf("s3+s3_idx=%s\n",s3+s3_idx);
        while(*(s3+s3_idx)!=32 && *(s3+s3_idx)!=0 && *(s3+s3_idx)!='\n'){
            *(temp+k)=*(s3+s3_idx); k++; s3_idx++;
        }
//        printf("temp(%d)=%s\n",i,temp);
        ((arr+cnt)->id)=cnt;
        strcpy((arr+cnt)->key,temp);
        ((arr+cnt)->v)=*(p+cnt+1);
        s3_idx++;
        cnt++; 
    }
//    print_tn(arr,m);
    int total=0;
    int s2_idx=0;
    for(i=0;i<n;i++){
        char* temp2=(char*)malloc(sizeof(char)*22);
        if (temp2==NULL){
            printf("memory allocation to temp2(%d) failed.\n",i);
            exit(1);
        }
        int k=0;
//        printf("s2+s2_idx=%s\n",s2+s2_idx);
        while(*(s2+s2_idx)!=32 && *(s2+s2_idx)!=0 && *(s2+s2_idx)!='\n'){
            *(temp2+k)=*(s2+s2_idx); k++; s2_idx++;
        }
        s2_idx++;
//        printf("temp2(%d)=%s\n",i,temp2);
        int flag=0,price=0;
        for(int j=0;j<m;j++){
            if(strcmp(temp2,(arr+j)->key)==0){
                flag=1;
                price=(arr+j)->v;
                break;
            }
        }
        if(flag==1){
//            printf("%s: price=%d\n",temp2,price);
            total+=price;
        }else{
//            printf("%s: price=%d\n",temp2,*p);
            total+=*p;
        }
    }
    printf("%d\n",total);
    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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        if (*(s+i)<48 || *(s+i)>57){i++;}
        else{
            int bidx=i;
            while(*(s+i)>=48 && *(s+i)<=57){
                i++;
            }
            int t=0;
            for(int j=bidx;j<i;j++){
                t*=10;
                t+=*(s+j)-48;
            }
        *(a+aidx++)=t;
        }
    }
    if (aidx!=n)printf("n!=aidx\n");
}
void print_ivector(int* a,int n){
    for(int i=0;i<n-1;i++){
        printf("%d ",*(a+i));
    }
    printf("%d\n",*(a+n-1));
}
void print_tn(tn* tns, int n){
    for(int i=0;i<n;i++){
        printf("----\n");
        printf("id=%d\n",(tns+i)->id);
        printf("key=%s\n",(tns+i)->key);
        printf("price=%d\n",(tns+i)->v);
    }
}
    

AtCoderで提出するとsample1の一つだけWAになるのだが、sample1のデータを入れて実行すると、正しい答えが出ている。原因が分からないので、StackOverflowで質問した。

actorbugさんから頂いた回答によると、strcmpで比較するために文字列を入れたtemp,temp2の最後に\0を書き込んでいなかったのが原因らしい。(他にも幾つか修正すべき点を指摘していただいた。)

ちなみに、この比較の部分は、最初はCの標準関数のstrcmpを使うのではなく、自作関数で比較しようとしたのだが、構造体のメンバである文字列のアドレスを主とルする方法が分からず、strcmpを使うことにしたのだった。

指摘されたとおりに修正したら、全てACになった。そのコードが以下。

typedef struct {
    int id;
    char key[21];
    int v;
    struct tn *l;
    struct tn *r;
} tn;
void print_tn(tn* tns, int n);
void input_iarray(char* s,int* a,int n,int imax);
int readint(char *s);
void print_ivector(int* a,int n);
int tree_to_pa(tn* f, int* a1, int* a2);
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N1 9
#define N2 2102
#define N 100000002
tn* create_tn(int key);
void insert_tn(int key, tn *node);
tn *root=NULL;
int cnt=0,ar_idx=0;
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    if(s1==NULL){
        printf("Memory allocation to s1 failed.");
        exit(1);
    }
    fgets(s1,N1,stdin);
    int i=0;
    int n=readint(s1);
    while(*(s1+i)!=32)i++;
    i++;
    int m=readint(s1+i);
//    printf("n=%d m=%d\n",n,m);
    free(s1);
    char* s2=(char*)malloc(sizeof(char)*(21*n+1));
    if(s2==NULL){
        printf("Memory allocation to s2 failed.");
        exit(1);
    }
    fgets(s2,(21*n+1),stdin);
//    printf("s2=%s\n",s2);
    char* s3=(char*)malloc(sizeof(char)*(21*m+1));
    if(s3==NULL){
        printf("Memory allocation to s3 failed.");
        exit(1);
    }
    fgets(s3,(21*m+1),stdin);
//    printf("s3=%s\n",s3);
    char* s4=(char*)malloc(sizeof(char)*(6*(m+1)+1));
    if(s4==NULL){
        printf("Memory allocation to s4 failed.");
        exit(1);
    }
    fgets(s4,6*(m+1)+1,stdin);
//    printf("s4=%s\n",s4);
    int* p=(int*)malloc(sizeof(int)*(m+1));
    if (p==NULL){
        printf("memory allocation to p failed.\n");
        exit(1);
    }
    input_iarray(s4,p,m+1,6*m+1);
//    print_ivector(p,m+1);
    free(s4);
    tn* arr=(tn*)malloc(sizeof(tn)*m);
    if (arr==NULL){
        printf("memory allocation to arr failed.\n");
        exit(1);
    }
    int s3_idx=0;
    i=0;
    for(;i<m;i++){
        char* temp=(char*)malloc(sizeof(char)*22);
        if (temp==NULL){
            printf("memory allocation to temp(%d) failed.\n",i);
            exit(1);
        }
        int k=0;
//        printf("s3+s3_idx=%s\n",s3+s3_idx);
        while(*(s3+s3_idx)!=32 && *(s3+s3_idx)!=0 && *(s3+s3_idx)!='\n'){
            *(temp+k)=*(s3+s3_idx); k++; s3_idx++;
        }
        *(temp+k)=0;
//        printf("temp(%d)=%s\n",i,temp);
        ((arr+cnt)->id)=cnt;
        strcpy((arr+cnt)->key,temp);
        free(temp);
        ((arr+cnt)->v)=*(p+cnt+1);
        s3_idx++;
        cnt++; 
    }
    free(s3);
//    print_tn(arr,m);
    int total=0;
    int s2_idx=0;
    for(i=0;i<n;i++){
        char* temp2=(char*)malloc(sizeof(char)*22);
        if (temp2==NULL){
            printf("memory allocation to temp2(%d) failed.\n",i);
            exit(1);
        }
        int k=0;
//        printf("s2+s2_idx=%s\n",s2+s2_idx);
        while(*(s2+s2_idx)!=32 && *(s2+s2_idx)!=0 && *(s2+s2_idx)!='\n'){
            *(temp2+k)=*(s2+s2_idx); k++; s2_idx++;
        }
        *(temp2+k)=0;
        s2_idx++;
//        printf("temp2(%d)=%s\n",i,temp2);
        int flag=0,price=0;
        for(int j=0;j<m;j++){
            if(strcmp(temp2,(arr+j)->key)==0){
                flag=1;
                price=(arr+j)->v;
                break;
            }
        }
        free(temp2);
        if(flag==1){
//            printf("%s: price=%d\n",temp2,price);
            total+=price;
        }else{
//            printf("%s: price=%d\n",temp2,*p);
            total+=*p;
        }
    }
    free(s2);
    free(arr);
    free(p);
    printf("%d\n",total);
    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;
}
void input_iarray(char* s,int* a,int n,int imax){
    int aidx=0,i=0; 
    while(*(s+i)!=0 && i<imax){
        if (*(s+i)<48 || *(s+i)>57){i++;}
        else{
            int bidx=i;
            while(*(s+i)>=48 && *(s+i)<=57){
                i++;
            }
            int t=0;
            for(int j=bidx;j<i;j++){
                t*=10;
                t+=*(s+j)-48;
            }
        *(a+aidx++)=t;
        }
    }
    if (aidx!=n)printf("n!=aidx\n");
}
void print_ivector(int* a,int n){
    for(int i=0;i<n-1;i++){
        printf("%d ",*(a+i));
    }
    printf("%d\n",*(a+n-1));
}
void print_tn(tn* tns, int n){
    for(int i=0;i<n;i++){
        printf("----\n");
        printf("id=%d\n",(tns+i)->id);
        printf("key=%s\n",(tns+i)->key);
        printf("price=%d\n",(tns+i)->v);
    }
}
    

ハッシュ

ABC319-A

問題概要

文字列と数値の組の表がある。表中の文字列の一つが与えられるので、それに対応する数値を出力せよ。

解答

data=<<EOF
tourist 3858
ksun48 3679
Benq 3658
Um_nik 3648
apiad 3638
Stonefeang 3630
ecnerwala 3613
mnbvmar 3555
newbiedmy 3516
semiexp 3481tourist 3858
EOF
d=data.split("\n")
da=Array.new(d.size){Array.new(2)}
i=0
d.each do |it|
    s,n=it.split(" ")
    da[i][0]=s
    da[i][1]=n.to_i
    i+=1
end
s=gets.chomp
da.each do |dd|
    if dd[0]==s
        puts dd[1]
        exit
    end
end
    

上が本番の提出コード。Cでやったのが以下。

typedef struct {
    char key[21];
    int v;
} tn;
void print_tn(tn* tns, int n);
int readint(char *s);
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N1 10
#define N2 12
#define N 100000002
int cnt=0,ar_idx=0;
int main(void){
    char s1[]="tourist 3858,ksun48 3679,Benq 3658,Um_nik 3648,apiad 3638,Stonefeang 3630,ecnerwala 3613,mnbvmar 3555,newbiedmy 3516,semiexp 3481";
//    printf("%s\n",s1);
    tn* arr=(tn*)malloc(sizeof(tn)*N1);
    if (arr==NULL){
        printf("memory allocation to arr failed.\n");
        exit(1);
    }
    int s1_idx=0,i=0;
    for(i=0;i<N1;i++){
        char* temp=(char*)malloc(sizeof(char)*22);
        if (temp==NULL){
            printf("memory allocation to temp(%d) failed.\n",i);
            exit(1);
        }
        int k=0;
        while(*(s1+s1_idx)!=32){
            *(temp+k)=*(s1+s1_idx); k++; s1_idx++;
        }
        *(temp+k)=0;
        strcpy((arr+i)->key,temp);
        free(temp);
        s1_idx++;
        int point=readint(s1+s1_idx);
        (arr+i)->v=point;
        if (i!=N1-1){
            while(*(s1+s1_idx)>=48 && *(s1+s1_idx)<=57){
                s1_idx++;
            }
            while(*(s1+s1_idx)==44)s1_idx++;
        }
    }
//    print_tn(arr,N1);
    char* s2=(char*)malloc(sizeof(char)*N2);
    if (s2==NULL){
        printf("memory allocation to s2 failed.\n");
        exit(1);
    }
    fgets(s2,N2,stdin);
    char* temp2=(char*)malloc(sizeof(char)*(N2+1));
    if (temp2==NULL){
        printf("memory allocation to temp2 failed.\n");
        exit(1);
    }
    int s2_idx=0,k=0;
    while((*(s2+s2_idx)>=48 && *(s2+s2_idx)<=122)){
        *(temp2+k)=*(s2+s2_idx); s2_idx++; k++;
    }
    *(temp2+k)=0;
//    printf("%s\n",temp2);
    int flag=0;
    for(i=0;i<N1;i++){
        if (strcmp((arr+i)->key,temp2)==0){
            printf("%d",(arr+i)->v);
            flag=1;
            exit(0);
        }
    }
    if (flag==0){
        printf("name couldn't be found.\n");
        exit(1);
    }
    free(arr);
    free(temp2);
    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;
}
void print_tn(tn* tns, int n){
    for(int i=0;i<n;i++){
        printf("----\n");
        printf("key=%s\n",(tns+i)->key);
        printf("point=%d\n",(tns+i)->v);
    }
}
    

ABC325-B

問題概要

拠点iの標準時は世界標準時+w[i]で、x[i]人が所属している。(w[i]は整数。)整数時から始まる1時間に各拠点の標準時における9時から18時が完全に含まれる拠点の所属員を累計するとき、その値の最大値を求めよ。

解答

n=gets.to_i
x=[]
w=[]
n.times do |i|
    x[i],w[i]=gets.chomp.split(" ").map(&:to_i)
end
h=Hash.new
(0..23).each do |i|
    t=0
    n.times do |j|
        st=i+w[j]
        if st>24
            st-=24
        end
        if st>=9 && st<=17
            t+=x[j]
        end
    end
    h[i]=t
end
max=0
h.each {|key, value| 
    if value > max
        max=value
    end
}
puts max
    

この問題では開始時間は整数時の24通りに限られるので、0..23時をそれぞれ開始時間としたときに開始時間の現地標準時が9時から17時になる拠点の所属員数の累計値を、開始時間をキー、対応する累計値を値とするハッシュにまとめ、値の最大値を出力すればよい。

上が本番の提出コード。Cでやったのが以下。

typedef struct {
    int hour;
    int num;
} table;
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 6
#define N2 12
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 n=readint(s1);
    free(s1);
    int* w=(int*)malloc(sizeof(int)*n);
    if(w==NULL){
        printf("memory allocation to w failed\n");
        exit(1);
    }
    int* x=(int*)malloc(sizeof(int)*n);
    if(x==NULL){
        printf("memory allocation to x failed\n");
        exit(1);
    }
    for(int i=0;i<n;i++){
        char* s2=(char*)malloc(sizeof(char)*N2);
        if(s2==NULL){
            printf("memory allocation to w failed\n");
            exit(1);
        }
        fgets(s2,N2,stdin);
        *(x+i)=readint(s2);
        int j=0;
        while(*(s2+j)!=32)j++;
        j++;
        *(w+i)=readint(s2+j);
//        printf("w[%d]=%d x[%d]=%d\n",i,*(w+i),i,*(x+i));
        free(s2);
    }
    table* t=(table*)malloc(sizeof(table)*24);
    if(t==NULL){
        printf("memory allocation to t failed\n");
        exit(1);
    }
    for(int i=0;i<24;i++){
        (t+i)->hour=i;
    }
    for(int i=0;i<n;i++){
        int start=9;
        int end=17;
        start=((start+*(w+i))%24);
        end=((end+*(w+i))%24);
//        printf("start=%d end=%d\n",start,end);
        if(start<end){
            for(int j=start;j<=end;j++){
                ((t+j)->num) += *(x+i);
            }
        }else{
            for(int j=start;j<=23;j++){
                ((t+j)->num) += *(x+i);
            }
            for(int j=0;j<=end;j++){
                ((t+j)->num) += *(x+i);
            }
        }
    }
    int max=0;
    for(int i=0;i<24;i++){
        //printf("from %d: %d person\n",i,(t+i)->num);
        if ((t+i)->num > max){
            max=(t+i)->num;
        }
    }
    printf("%d\n",max);
    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;
}
    

ほぼ出来上がったころに気づいたのだが、構造体tableのhourはインデックスと同じなので、構造体を作らなくても単なる配列でもよかった。


複数の最大値

ABC329-D

問題概要

1からnまでの整数のいずれかを要素とする配列(要素数m)が与えられる。i番目までの中で最も多く出現している数(ただし、最も多く出現している数が複数あるときは、そのうち最も小さい数)を出力せよ。

解答

n,mは最大で2*10^5なので、ほぼ二重ループになる下記のコードでTLEになることは分かっていたが、この日は開始から約30分後に参加登録したにもかかわらず、A,B問題はおろかC問題まであっさり解け、それで終了までかなり時間があったため、これもやってみることに。あくまでも、小規模なデータならこれで解けるということで書いたコードである。

n,m=gets.chomp.split(" ").map(&:to_i)
a=gets.chomp.split(" ").map(&:to_i)
c=Hash.new
(0...m).each do |i|
    if c.has_key?(a[i])
        c[a[i]]+=1
    else
        c[a[i]]=1
    end
    max=0
    idxs=[]
    c.each do |k,v|
        if v>max
            idxs.clear
            idxs.push(k)
            max=v
        elsif v==max
            idxs.push(k)
        end
    end
    puts idxs.min
end
    

i番目まで数えた時の各要素の出現回数を、要素の値をキー、出現回数を値とするハッシュで保持、最大回数出現する要素を配列に記録し、その配列の最小値を出力している。

2022年11月10日木曜日

出力形式に関する問題

小数の出力形式

ABC274-A

問題概要

A/Bを小数第4位で四捨五入した値を末尾ゼロを省略せずに表記して出力せよ。

解答

a,b=gets.chomp.split(" ").map(&:to_i)
printf("%.3f\n", (b.to_f/a).round(3))
    

aかbのどちらかを小数に変換して割り算し、そのオブジェクトからroundメソッド呼び出して引数に表示したい小数点以下の桁数を渡す。

あとはprintfが0埋めをやってくれる。

どうということのない問題なのだが、実際には改めて調べなおしてやることになった。

上が本番の提出コード。Cでやったのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 7
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 j=0;
    while(*(s1+j)!=32)j++;
    j++;
    int b=readint(s1+j);
    free(s1);
//    printf("a=%d, b=%d\n",a,b);
    int quot10000=b*10000/a;
//    printf("quot10000=%d\n",quot10000);
    int arr[5];
    for(int i=0,divisor=10000;i<5,divisor>0;divisor/=10,i++){
        arr[i]=quot10000/divisor;
        quot10000-=arr[i]*divisor;
//        printf("arr[%d]=%d\n",i,arr[i]);
    }
    char str[6];
    str[0]=arr[0]+48;
    str[1]=46;
    str[2]=arr[1]+48;    
    str[3]=arr[2]+48;
    if (arr[4]<5){
        str[4]=arr[3]+48;
    }else{
        str[4]=arr[3]+1+48;
    }
    str[5]=0;
    printf("%s\n",str);
    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;
}
    

16進数に変換し、先頭に0埋め

ABC271-A

問題概要

10進法で表された整数nが与えられる。(1≦n≦255) nを二桁の16進数として出力せよ。ただし、二桁未満になる時は、先頭に0を埋める。

解答

n=gets.to_i
s=[]
q,x=n.divmod(16)
h={0=>"0",1=>"1",2=>"2",3=>"3",4=>"4",5=>"5",6=>"6",7=>"7",8=>"8",9=>"9",10=>"A",11=>"B",12=>"C",13=>"D",14=>"E",15=>"F"}
s.push(h[q])
s.push(h[x])
puts s.join("")
    

これが本番の提出コード。

0から15の数字をキー、"0"から"F"までの文字を値とするハッシュを作り、与えられた数字を16で割った商をキーとする値を16の位に、余りをキーとする値を1の位に入れている。

もっとも、rubyにはもともと10進数を16進数に変換する機能が備わっているので、以下のようにそちらを利用してもよい。

n=gets.to_i
nn=sprintf("%02X",n)
puts nn
    

本番でこれを使わなかったのは、これだと先頭に0xが入ると思い込んでいたからなのだが、やってみたら0x無しで出力できた。

Cでやったのが以下。

char convert_to_hexadeca_1(int n);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 5
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 n=readint(s1);
//    printf("n=%d\n",n);
    int n1=n/16;
    int n2=n%16;
//    printf("s0=%d, s1=%d\n",n1,n2);
    char str[3];
    str[0]=convert_to_hexadeca_1(n1);
    str[1]=convert_to_hexadeca_1(n2);
    str[2]=0;
    printf("%s\n",str);
    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;
}
char convert_to_hexadeca_1(int n){
    if (n>=16){
        printf("n must be less than 16.\n");
        exit(1);
    }
    if (n<0){
        printf("n must not be negative.\n");
        exit(1);
    }
    char c;
    if (n>=0 && n<10){
        c=n+48;
    }else if (n>9 && n<16){
        c=n-10+65;
    }
    return c;
}
    

時刻の表示

ABC258-A

問題概要

21時からk分後の時刻をhh:mmの形で出力せよ。

解答

n=gets.to_i
m=s=0
m,s=n.divmod(60)
m+=21
printf("%02d:%02d\n",m,s)
    

上が本番の提出コード。Cでやったのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 5
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 k=readint(s1);
//    printf("k=%d\n",k);
    int hour=21+k/60;
    int minute=k%60;
//    printf("hour=%d, minute=%d\n",hour,minute);
    char str[6];
    str[0]=hour/10+48;
    str[1]=hour%10+48;
    str[2]=58;
    str[3]=minute/10+48;
    str[4]=minute%10+48;
    str[5]=0;
    printf("%s\n",str);
    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;
}
    

先頭0埋め

ABC254-A

問題概要

3桁以上の整数が与えられる。その下2桁を、xxの形で出力せよ。

解答

n=gets.to_i
t1=(n%10).to_s
n/=10
ans=(n%10).to_s
ans+=t1
puts ans    
    

上が本番の提出コード。

1の位の数と10の位の数をいったん文字列にしてから連結している。

後になって考えると、次のようにした方が簡単だったのだが。

n=gets.to_i
printf("%02d\n",n%100)
    

Cでやったのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 5
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 n=readint(s1);
//    printf("n=%d\n",n);
    int n10=(n/10)%10;
    int n1=n%10;
//    printf("n10=%d, n1=%d\n",n10,n1);
    char str[3];
    str[0]=n10+48;
    str[1]=n1+48;
    str[2]=0;
    printf("%s\n",str);
    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;
}    
    

切り捨て

ABC314-A

問題概要

3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679の小数第n位以下を切り捨てて出力せよ。

解答

最初、問題を、「小数第n+1位で四捨五入せよ」というものだと勘違いしてしまって書いたのが次のコード

n=gets.to_i
s="3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679"
ans=""
t=s[n+2].to_i
if t>=5
    ss=s[n+1].to_i
    ss+=1
    if ss==10
        s[n+1]="0"
        sss=s[n].to_i+1
        if sss==10
            s[n]="0"
            s[n-1]=(s[n-1].to_i+1).to_s
        else
            s[n].sss.to_s
        end
    else
        s[n+1]=ss.to_s
    end
end
(2+n).times do |i|
    ans[i]=s[i]
end
puts ans
    

9が最大で何個まで続いているか分からなければまた別の処理が必要になるが、この問題の文字列では9は2つまでしか連続していないから、これで十分。なぜかWAがいくつか出るので、いったん放棄していたのだが、B問題をやったあと改めて見直してみて問題の勘違いに気付いた。それで書き直したのが以下。

n=gets.to_i
s="3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679"
ans=""
t=s[n+2].to_i
(2+n).times do |i|
    ans[i]=s[i]
end
puts ans
    

これでACになった。有り得ないような勘違いだったのだが、まさか単なる切り捨てのような簡単すぎる問題は出ないだろうという思い込みで問題文を読み違えてしまったようだ。

Cでやったのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 5
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 n=readint(s1);
//    printf("n=%d\n",n);
    char pi[]="3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679";
    char str[103];
    str[0]=pi[0];
    str[1]=pi[1];
    for(int i=1;i<=n;i++){
        str[i+1]=pi[i+1];
    }
    str[n+2]=0;
    printf("%s\n",str);
    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;
}
    

2022年11月9日水曜日

累乗、階乗、C,Pの計算

2^nの計算

ABC256-A

問題概要

2^nを出力せよ。

解答

n=gets.to_i
puts 2.pow(n).to_i
    

としてもいいし、底が2のときたけ使える方法だが、

n=gets.to_i
puts 1<<n
    

としてもいい。

このときは本番不参加。Cでやったのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 4
int main(void){
    char* s1=(char*)malloc(sizeof(char)*N1);
    fgets(s1,N1,stdin);
    int n=readint(s1);
    int t=1,p=2;
    while(n>0){
        if(n%2)t*=p;
        n>>=1;
        p*=p;
    }
    printf("%d\n",t);
    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;
}    

累乗

ABC283-A

問題概要

aのb乗を出力せよ。

解答

a,b=gets.chomp.split(" ").map(&:to_i)
puts a**b
    

上が本番で提出したコード。Cでやったのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N 5
int main(void){
    char* s=(char*)malloc(sizeof(char)*N);
    fgets(s,N,stdin);
    int a=readint(s);
    int i=0;
    while(*(s+i)!=32)i++;
    i++;
    int b=readint(s+i);
    free(s);
    int t=1;
    for(int j=0;j<b;j++){
        t*=a;
    }
    printf("%d\n",t);
    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;
}
    

累乗を求める部分を関数として独立して使えるよ絵にしたのが以下。

#include<stdio.h>
#include<stdlib.h>
#define N 5
int readint(char *s);
int int_pow(int x,int y);
int main(void){
    char* s=(char*)malloc(sizeof(char)*N);
    fgets(s,N,stdin);
    int a=readint(s);
    int i=0;
    while(*(s+i)!=32)i++;
    i++;
    int b=readint(s+i);
    free(s);
    printf("%d\n",int_pow(a,b));
    return 0;
}
int 
int_pow(int x,int n){
    int ret=1; 
    while(n>0){
        if((n&1)==1)ret*=x;
        x*=x;
        n>>=1;
    }
    return ret;
}
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;
}
    

指数関数の不等式を解く

ABC248-B

問題概要

a*k^n≧bを満たす最小の非負整数nを求めよ。(a,b,kは自然数。k≧2)

解答

a,b,k=gets.chomp.split(" ").to_a.map(&:to_i)
i=0
loop do
    if a>=b
        puts i
        exit
    end
    a*=k
    i+=1
end
    

これが本番で提出したコード。

このようにループを回して不等式を満たす最小の整数iを求めたわけだが、直接、上の不等式を解くという方法もある。それでやってみたのが以下のコードなのだが、一つだけWAになった。(Sample:AC x 3,All:AC x 9 WA x 1)

a,b,k=gets.chomp.split(" ").to_a.map(&:to_i)
puts Math::log((b/a.to_f),k).ceil
    

浮動小数点数の誤差の影響かと思うが、確実な理由は分からない。

--------------------

(追記)StackOverflowで質問したところ、やはり浮動小数の誤差が原因だった。


n!を求める

ABC273-A

問題概要

f(0)=1, f(k)=k*f(k-1)である。(k>0) nが与えられたとき、f(n)を求めよ。ただし、n≦10

再帰関数を使ってn!を求める問題である。

解答

    def f(n)
    if n==1 || n==0
        ret=1
    else
        ret=n*f(n-1)
    end
    return ret
end
n=gets.to_i
puts f(n)
    

上は、本番で提出したコード

問題文でもnが10以下という限定が付いているが、この方法でn!を求めると、すぐにnが巨大な数になってしまう。

実用的には、あらかじめn!を小さい順に計算してpushした配列を作っておいて、それを参照するのがよいか。

こんな感じで。

$fac=[]
$fac[0]=1
(1..100).each do |i|
    $fac[i]=$fac[i-1]*i
end
n=gets.to_i
puts $fac[n]
    

Cでやったのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N 4
int main(void){
    char* s=(char*)malloc(sizeof(char)*N);
    fgets(s,N,stdin);
    int n=readint(s);
    free(s);
    int t=1;
    for(int j=1;j<=n;j++){
        t*=j;
    }
    printf("%d\n",t);
    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;
}
    

パスカルの三角形を描く

ABC254-B

問題概要

次の定義を使ってパスカルの三角形を描け。

  1. j=0またはj=iのとき、a[i,j]=1
  2. 上記以外の時、a[i,j]=a[i-1,j]+a[i-1]

n=gets.to_i
a=[]
n.times do |i|
    a.push(Array.new(i+1,1))
    if i!=0
        (i+1).times do |j|
            if j!=0 && j!=i
                a[i][j]=a[i-1][j-1]+a[i-1][j]
            end
        end
    end
        puts a[i].join(" ")
end
    

問題文の指示通りに実装すればよい。

それ自体としては面白みのない問題だが、これを使ってCの計算をすることができる。

C[n,r]を、C[n,r]=n!/r!・(n-r)!という定義に従って計算しようとすると、途中で出てくる数が大きくなりすぎてしまう難点があるのだが、上のやり方でパスカルの三角形を作れば、a[i-1][j]がそのままC[i,r]になる。

それを使ってCの計算をやってみた。

    
n=100
$a=[]
n.times do |i|
    $a.push(Array.new(i+1,1))
    if i!=0
        (i+1).times do |j|
            if j!=0 && j!=i
                $a[i][j]=$a[i-1][j-1]+$a[i-1][j]
            end
        end
    end
end
def C(n,r)
    if r>n/2
        r=n-r
    end
    $a[n][r]
end
11.times do |i|
    puts "C[10][#{i}]=#{C(10,i)}"
end
    

実行結果は、

C[10][0]=1
C[10][1]=10
C[10][2]=45
C[10][3]=120
C[10][4]=210
C[10][5]=252
C[10][6]=210
C[10][7]=120
C[10][8]=45
C[10][9]=10
C[10][10]=1

Cで書いた、元の問題の解答が以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 4
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 n=readint(s1);
    free(s1);
    int sz=n*(n+1)/2;
    int* p=(int*)malloc(sizeof(int)*sz);
    *p=1;
    *(p+1)=1;
    *(p+2)=1;
    int start=3;
    for(int i=2;i<n;i++){
        *(p+start)=1;
        *(p+start+i)=1;
        for(int j=1;j<i;j++){
            *(p+start+j)=*(p+start-i+j-1)+*(p+start-i+j);
//            printf("%d+%d=%d\n",*(p+start-i+j-1),*(p+start-i+j),*(p+start+j));
        }
        start+=(i+1);
    }
    start=0;
    for(int i=0;i<n;i++){
        for(int j=0;j<i+1;j++){
            printf("%d",*(p+start+j));
            if(j==i){
                printf("\n");
            }else{
                printf("%c",32);
            }
        }
        start+=(i+1);
    }
    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;
}
    

上に手を加えてnCrを求めるものを書いたのが以下。

int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N 30
int main(void){
    int* p=(int*)malloc(sizeof(int)*N*(N+1)/2);
    *p=1;
    *(p+1)=1;
    *(p+2)=1;
    int start=3;
    for(int i=2;i<N;i++){
        *(p+start)=1;
        *(p+start+i)=1;
        for(int j=1;j<i;j++){
            *(p+start+j)=*(p+start-i+j-1)+*(p+start-i+j);
        }
        start+=(i+1);
    }
    for(int i=1;i<N;i++){
        int s=i*(i+1)/2;
        for(int j=0;j<i+1;j++){
            printf("C[%d,%d]=%d\n",i,j,*(p+s+j));
        }
    }
    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;
}
    

ABC303-B

問題概要

要素数nの整数列がm個与えられる。その中に、一度も隣接していない数字の組はいくつあるか

解答

以下のコードは、一部RE、一部WAになった。

def C(n,r)
    if r==0 || r==n
        return 1
    elsif r==1 || r==n-1
        return n
    else
        return C(n-1,r)+C(n-1,r-1)
    end
end
require 'set'
n,m=gets.chomp.split(" ").map(&:to_i)
a=[]
m.times do |i|
    a[i]=gets.chomp.split(" ").map(&:to_i)
end
b=Array.new(n){Array.new(n,false)}
m.times do |i|
    (n-1).times do |j|
        b[a[i][j]-1][a[i][j+1]-1]=true
        b[a[i][j+1]-1][a[i][j]-1]=true
    end
end
s=Set.new
m.times do |i|
    n.times do |j|
        if b[i][j]==true
            if j<i
                s.add([j,i])
            else
                s.add([i,j])
            end
        end
    end
end
puts C(n,2)-s.size
    

隣接している組の数を数え(たつもりだった)、それを、n人から2人を選ぶ場合の数C[n,2]から引いている。

後から見直してみると上のコードは隣接しているかの判定とは全然関係の無いことをやっているので、やり直したのが以下、これでACになった。

def C(n,r)
    if n<=0 || r<0 || n<r
        puts "wrong argument"
    elsif r==0 || r==n
        return 1
    elsif r==1 || r==n-1
        return n
    else
        return C(n-1,r)+C(n-1,r-1)
    end
end
require 'set'
n,m=gets.chomp.split(" ").map(&:to_i)
ar=[]
m.times do |i|
    ar[i]=gets.chomp.split(" ").map(&:to_i)
end
s=Set.new
m.times do |i|
    (1...n).each do |j|
        a=ar[i][j-1]
        b=ar[i][j]
        if a>b
            a,b=b,a
        end
        s.add([a,b])
    end
end
puts C(n,2)-s.size
=begin
puts C(n,2)
s.each do |st|
    print "(#{st[0]},#{st[1]}), "
end
=end
    

Cでやったのが以下。

int Combi(int n,int r);
int readint(char *s);
#include<stdio.h>
#include<stdlib.h>
#define N1 7
#define N 143
#define N2 5051
int main(void){
    int n,m;
    int i=0;
    char *s=(char*)malloc(sizeof(char)*N1);
    if (s==NULL){
        printf("memory allocation to s failed.\n");
        exit(1);
    }
    fgets(s,N1,stdin);
    n=readint(s);
    while(*(s+i)!=32)i++;
    i++;
    m=readint(s+i);
//    printf("%d %d\n",n,m);
    free(s);
    int* a=(int*)malloc(sizeof(int)*n*m);
    if (a==NULL){
        printf("memory allocation to a failed.\n");
        exit(1);
    }
    for(int j=0;j<m;j++){
        char *s2=(char*)malloc(sizeof(char)*N);
        fgets(s2,N,stdin);
        int l=0,k=0;
        while(k<n){
            int temp=readint(s2+l);
            *(a+j*n+k)=temp; k++;
            while(*(s2+l)>=48 && *(s2+l)<=57)l++;
            l++;
        }
    }
    /*
    for(int j=0;j<m;j++){
        for(int k=0;k<n;k++){
            printf("%d ",*(a+j*n+k));
        }
        printf("\n");
    }
    for(int j=0;j<=10;j++){
        printf("Combi(%d,%d)=%d\n",10,j,Combi(10,j));
    }
    */
    int cnt=0;
    int *b=(int*)malloc(sizeof(int)*N2);
    if (b==NULL){
        printf("memory allocation to b failed.\n");
        exit(1);
    }
    for(int j=0;j<N;j++){
        *(b+j)=0;
    }
    for(int j=0;j<m;j++){
        for(int k=1;k<n;k++){
            int t1=*(a+j*n+k-1);
            int t2=*(a+j*n+k);
            if(t1>t2){
                int temp=t1;
                t1=t2;
                t2=temp;
            }
            int t3=t1*100+t2;
//            printf("(%d, %d)\n",t1,t2);
            *(b+t3)=1;
        }
    }
    for(int j=0;j<N2;j++){
        if(*(b+j)==1)cnt++;
    }
//    printf("Combi(%d,%d)=%d\n",n,2,Combi(n,2));
//    printf("cnt=%d\n",cnt);
    printf("%d\n",Combi(n,2)-cnt);
    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 Combi(int n,int r){
    if (n<=0 || r<0 || n<r){
        printf("Wrong argument.\n");
        exit(1);
    }
    if (r==0 || r==n)return 1;
    else if (r==1 || r==n-1)return n;
    else return Combi(n-1,r)+Combi(n-1,r-1);
}
    

基本的にはrubyでやったときと同じやり方で、C(n,2)から隣接している組の数を引いているのだが、問題は、Cでセットを実装する力はまだないということである。しかし、問題においてはnが50とかなり小さいので、隣接する二つの数を、小さい方を上2桁、大きい方を下2桁とする4桁の整数と一対一対応させることができ、二つの数の組を一つの整数に置き換えて著すことができる。そうやって隣接する組の数を数えた。


累乗計算

ABC320-A

問題概要

A^B + B^A を計算せよ。

解答

a,b=gets.chomp.split(" ").map(&:to_i)
puts a**b + b**a
    

Cでやったのが以下。

#include<stdio.h>
#include<stdlib.h>
#define N 5
int readint(char *s);
int int_pow(int x,int y);
int main(void){
    char* s=(char*)malloc(sizeof(char)*N);
    if(s==NULL){
        printf("memory allocation to s failed\n");
        exit(1);
    }
    fgets(s,N,stdin);
    int a=readint(s);
    int i=0;
    while(*(s+i)!=32)i++;
    i++;
    int b=readint(s+i);
    free(s);
    printf("%d\n",int_pow(a,b)+int_pow(b,a));
    return 0;
}
int 
int_pow(int x,int n){
    int ret=1; 
    while(n>0){
        if((n&1)==1)ret*=x;
        x*=x;
        n>>=1;
    }
    return ret;
}
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;
}
    

ABC324-B

問題概要

与えられた整数nが、n=2^x * 3^y と表せるか。

解答

問題は上記のように表現されているが、x,yを具体的に求める必要はなく、要は2と3以外の素因数を持たなければよい。つまり、2と3で割れるだけ割った後、1になっていればよい。

n=gets.to_i
while n%2==0
    n/=2
end
while n%3==0
    n/=3
end
if n==1
    puts "Yes"
else
    puts "No"
end
    

Cでやったのが以下。

#include
#include
#define N 21
long long readll(char *s);
int main(void){
    char* s=(char*)malloc(sizeof(char)*N);
    if(s==NULL){
        printf("memory allocation to s failed\n");
        exit(1);
    }
    fgets(s,N,stdin);
    long long a=readll(s);
    free(s);
    while(a%2==0){
        a=a>>1;
    }
    while(a%3==0){
        a/=3;
    }
    if(a==1){
        printf("Yes\n");
    }else{
        printf("No\n");
    }
    return 0;
}
long long readll(char *s){
    long long ret=0;
    int i=0;
    while(*(s+i)>=48 && *(s+i)<=57){
        ret*=10;
        ret+=*(s+i)-48;
        i+=1;
    }
    return ret;
}
    

ABC327-B

問題概要

与えられた数bが、整数aにより、b=a^a と表せるか。

解答

まずは初めに書いたコード。TLEになった。

b=gets.to_i
if b==1
    puts 1
    exit
else
    (2..(Math::sqrt(b).to_i)).each do |a|
        if a**a==b
            puts a
            exit
        end
    end
end
puts -1
    

まあこれはTLEになるのも当然。B問題だから計算量のことは意識せずに書いたのだが、bは最大で10^18なので、その2乗根まで1つずつ試すと10^9回である。もっとも、この問題の場合、bが4でなければaはbの3乗根以下、27でもなければ4乗根以下なので、その辺までaの上限を下げてしまえば、簡単に回避できそうだ。

そうしてもよかったのだが、上のコードを書きながら、別のやり方もあるなと考えていたので、そちらで書いたのが以下。

    class Pair
    attr_accessor :a, :b
    def initialize(h, t)
        @a, @b = h, t
    end
end
b=gets.to_i
list=[]
a=1
loop do
    t=a**a
    if t&gt;1e18
        break
    else
        list.push(Pair.new(a,t))
        a+=1
    end
end
list.each do |ls|
    if ls.b==b
    puts ls.a
    exit
    end
end
puts -1
    

bは制約により1以上10^18以下だが、何しろbはaの二乗なのでaが大きくなるとすぐにこの上限に達する。なにしろ、a=10ですでにb=10^10なのだ。だから、aを1からはじめて1ずつインクリメントしながらa^aのリストを作っていって、それが10^18を超えたら停止し、リストの中にbが含まれているか調べればよい。出来たリストの中身を確認したところ、aは最大で15だった。


Cでやって、まず書いたのが以下のコード。これはREになった。

typedef struct {
    long long n;
    long long n2;
} table;
long long readll(char *s);
long long ll_pow(long long x, long long n);
#include<stdio.h>
#include<stdlib.h>
#define N1 21
#define N2 16
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);
    long long  b=readll(s1);
    free(s1);
    table* tb=(table*)malloc(sizeof(table)*N2);
    if(tb==NULL){
        printf("memory allocation to tb failed\n");
        exit(1);
    }
    for(int j=0;j<N2;j++){
        (tb+j)->n=0;
        (tb+j)->n2=0;
    }
    long long t1=1;
    long long t2=1;
    int i=0;
    while(1){
        t2=ll_pow(t1,t1);
        if (t2==0)break; 
        (tb+i)->n=t1;
        (tb+i)->n2=t2;
        t1+=1; i+=1;
    }
/*    for(int i=0;i<N2;i++){
        printf("%d :(%lld, %lld)\n",i+1,(tb+i)->n,(tb+i)->n2);
    }
*/
    int found=0;
    for(int j=0;j<N2;j++){
        if (b==(tb+j)->n2){
            found=1;
            break;
        }
    }
    free(tb);
    if(found==1){
        printf("Yes\n");
    }else{
        printf("No\n");
    }
    return 0;
}
long long readll(char *s){
    long long ret=0;
    int i=0;
    while(*(s+i)>=48 && *(s+i)<=57){
        ret*=10;
        ret+=*(s+i)-48;
        i+=1;
    }
    return ret;
}
long long ll_pow(long long x, long long n){
    long long ret=1; 
    while(n>0){
        if(ret>1e18 || x>1e18){
            return 0;
        }
        if((n&1)==1)ret*=x;
        x*=x;
        n>>=1;
    }
    return ret;
}
    

先にrubyでやったときは、型の最大値を超えてしまうことを心配する必要はなかったが、Cではlong long型の最大値である2^64を超えないようにする必要がある。

もっともこれは、先にrubyでやったときにaが15を超えるとa^aが10^18を超えることを確認しているので、15^15のところまででやめるようにすれば解決可能ではある。しかし、先に型の制約がない言語で一回試しているという前提がない条件で、Cでのコードの中だけで、「long long型の最大値を超えないようにしつつ、bが10^18である場合まですべて漏れなく試す。」という処理を書きたかった。

もちろん、先にrubyで試していなくでも、log10(15^15)=15*log10(15)=15*(log10(3)+1og10(5))=15*(log10(3)+1og10(10/2))=15*(log10(3)+1-1og10(2))=15*(0.4771+1-0.3010)≓17.7, log10(16^16)=log10(2^64)=64*log10(2)=64*0.3010≓19.2となって、15と16の間が境界であることは手計算で確認できる。10^10は明らかに10^18より小さく、20*20=2^20*10^20は明らかに10^18より大きいから、その中間あたりから試していけば、15と16の間が境界というのもすぐわかりはする。ただ、こういう人間による手計算の前処理を使うのもちょっと邪道な気がする。それに、log10(15)やlog10(16)は上のように高校生のころから記憶している数値を使ってすぐに計算できるが、これがlog10(17)やlog10(13)も調べなければならないとなると常用対数表が必要になるし、そういったコードの外の処理を伴わずに、全てをコードの中だけで判定したかったのである。aの上限N2を20としたのは、上述のように20^20が10^18より大きいことは対数計算をするまでもなく明白なので、これぐらいは人間の判断を介在させてもいいだろうと妥協した。

そこで、計算途中で本問のbの制約上の最大値である10^18を超えたら止める処理を累乗を計算する関数であるll_powの中に入れておいたが、なにしろ10^18は2^64にかなり近い数なので(上述のように、log10(2^64)≓19.2)、結局long long型の最大値を超えてしまうのではないかという不安は感じていたが、昨日は頭が疲れていたこともあり、これで提出してしまって、REになったのである。

それで、Stack StackOverflowで質問してしまって終わりにしたのだが、actorbugさんから回答を頂いたところ、やはり計算途中で桁あふれを起こしてしまい、それが原因のようだ。

a=16のとき、ll_pow関数内でi回ループを回した後のxの変化を見てみると

i n x
0 16 16
1 8  16*16=2^8
2 4  (2^8)*(2^8)=2^16
3 2  (2^16)*(2^16)=2^32
4 1  (2^32)*(2^32)=2^64  
    
となり、最後にret=1*2^64=2^64となるが、ここで4回目のループ終了時のxとretに10^18を超える値を代入してしまっている。そして、こうなることは上のコード内のll_pow関数内の処理では止められない。(こんなことは質問する前に自分で気付くべきだった。)

それで、今度はretかxが10^9を超えた時点で止めるようにコードを修正してみたのだが、そうするとnが14と15のときも0を返してまう。

こちらもll_pow関数内でi回ループを回した後のxとretの変化を見てみると、まずa=14のとき、

i   n   x ret
0   14  14  1
1   7   14^2   1
2   1   14^8    14^4   
となって、log10(14^8)=8*log10(14)=8*(log10(2)+log10(7))=8*(0.3010+0.8451)=8*1.1461≓9.2となってxが10^9を超え、ここで終わってしまう。

まずa=15のとき、

i   n   x ret
0   15  15  1
1   7   15^2   15
2   3   15^4    15^2   
3   1   15^8    15^4   
となって、log10(15^8)=8*log10(15)=8*(log10(3)+log10(5))=8*(0.4771+0.6990)=8*1.1761≓9.4となってxが10^9を超え、ここで終わる。

この二つの数の場合には、xが10^10以下まで許容するようにすればうまくいくのだが、そのように人間がいちいちあらかじめ計算して調整することが、果たしてプログラミングのやり方として好ましいものかどうか。

それで、結局、ll_pow関数の中の上限チェックの部分は削除し、aを15まで試して止めることにした。これでACになった。それが以下。なお、先のコードは解答の形式も違っていたので、そこも修正してある。

typedef struct {
    long long n;
    long long n2;
} table;
long long readll(char *s);
long long ll_pow(long long x, long long n);
#include<stdio.h>
#include<stdlib.h>
#define N1 21
#define N2 15
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);
    long long  b=readll(s1);
    free(s1);
    table* tb=(table*)malloc(sizeof(table)*N2);
    if(tb==NULL){
        printf("memory allocation to tb failed\n");
        exit(1);
    }
    for(int j=0;j<N2;j++){
        (tb+j)->n=0;
        (tb+j)->n2=0;
    }
    long long t1=1;
    long long t2=1;
    int i=0;
    for(int j=0;j<N2;j++){
        t2=ll_pow(t1,t1);
        (tb+j)->n=t1;
        (tb+j)->n2=t2;
        t1+=1;
    }
/*    for(int i=0;i<N2;i++){
        printf("%d :(%lld, %lld)\n",i+1,(tb+i)->n,(tb+i)->n2);
    }
*/
    int found=-1;
    for(int j=0;j<N2;j++){
        if (b==(tb+j)->n2){
            found=(tb+j)->n;
            break;
        }
    }
    free(tb);
    printf("%d\n",found);
    return 0;
}
long long readll(char *s){
    long long ret=0;
    int i=0;
    while(*(s+i)>=48 && *(s+i)<=57){
        ret*=10;
        ret+=*(s+i)-48;
        i+=1;
    }
    return ret;
}
long long ll_pow(long long x, long long n){
    long long ret=1; 
    while(n>0){
        if((n&1)==1)ret*=x;
        x*=x;
        n>>=1;
    }
    return ret;
}