壁打ちAtCoder

AtCoderの問題をひたすら解いてくブログです。思考やコードの書き方の私的備忘録として

Pythonでzip圧縮を行うコード

パスワードなしzip圧縮

ただのzip圧縮の場合。
zipfileという標準モジュールを利用する。

docs.python.org

import os
import zipfile

file_name = "sea"  # ファイル名(拡張子なし)
dir_path = "C:/Users/user_name/Desktop"  # ファイルのあるディレクトリのパス
src = os.path.join(dir_path, file_name + ".png")  # 圧縮するファイルのパス(file_name+拡張子)
zip_path = os.path.join(dir_path, file_name + ".zip")  # 圧縮後のファイルのパス

with zipfile.ZipFile(zip_path, "w") as zf:
    zf.write(src, arcname=os.path.basename(src))
# 第一引数のファイルを、第二引数の名前でzipファイルに書き込む(省略可)

パスワードなしzipファイル解凍

zipfileを用いて解凍もできる。

import os
import zipfile

zip_path = "C:/Users/user_name/Desktop/sea.zip"  # 解凍するzipファイルのパス
dir_path = os.path.dirname(zip_path)  # 解凍先のパス

with zipfile.ZipFile(zip_path, "r") as zf:
    zf.extractall(path=dir_path)

パスワード付きzip圧縮

zipfileではパスワードを付けられないので、pyminizipというモジュールを使う。
pipでインストールする必要あり。

pip install pyminizip

Visual C++がないとinstallで失敗するらしいので、その旨のエラーが出たら、まずはC++をインストールする。

Desktopにあるsea.pngというファイルを圧縮してsea.zipにするコード

import os
import pyminizip

file_name = "sea"  # ファイル名(拡張子なし)
dir_path = "C:/Users/user_name/Desktop"  # ファイルのあるディレクトリのパス
src = os.path.join(dir_path, file_name + ".png")  # 圧縮するファイルのパス(file_name+拡張子)
zip_path = os.path.join(dir_path, file_name + ".zip")  # 圧縮後のファイルのパス
pyminizip.compress(src.encode("cp932"), "", zip_path.encode("cp932"), "password", 5)

ファイル名に日本語が含まれる場合は.encode("cp932")が必要らしい(参考記事②より)。
これはWindowsがcp932を使っているためらしいので、Windows以外なら不要なのかもしれない(試してないのでわからないけど)
上述のコードでzip圧縮した場合、解凍する時にファイル名が文字化けしてしまうので、少し工夫してやる必要がある。

パスワード付きzipファイル解凍

圧縮と異なり、解凍の方はzipfileでできる。(圧縮もできて欲しいんですけど……

import os
import zipfile

zip_path = "C:/Users/user_name/Desktop/sea.zip"  # 解凍するzipファイルのパス
dir_path = os.path.dirname(zip_path)  # 解凍先のパス
with zipfile.ZipFile(zip_path, "r") as zp:
    zp.extractall(path=dir_path, pwd="password".encode("utf-8"))

zip圧縮時にencode("cp932")を用いた場合はファイル名のエンコードデコードをいじってやる必要がある(参考記事③より)。

import os 
import zipfile

zip_path = "C:/Users/user_name/Desktop/海.zip"  # 解凍するzipファイルのパス
dir_path = os.path.dirname(zip_path)  # 解凍先のパス

with zipfile.ZipFile(zip_path, "r") as zp:
    for info in zp.infolist():
        info.filename = info.orig_filename.encode("cp437").decode("cp932")
        if os.sep != "/" and os.sep in info.filename:
            info.filename = info.filename.replace(os.sep, "/")
        zp.extract(member=info, path=dir_path, pwd="password".encode("utf-8"))

Python(TCP server) -> Android/Kotlin(TCP client)へ画像送信

ひとまず送れたということで殴り書き。
やりたいことを成し遂げるまでは課題は山のようにある。

・分割しなくても1度に送受信できる大きさの画像(1773Byte)
・画像のパスはpythonのコードと同じ階層

Python

from PIL import Image
import socketserver
import io

#ひとまず同じ階層に置いた
img = Image.open('palm32.png')


img_bytes = io.BytesIO()
img.save(img_bytes,format="PNG")
img_bytes = img_bytes.getvalue()

#print(len(img_bytes))  #len(img_bytes)でバイトサイズを取得する


class MyTCPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        self.data = self.request.recv(1024).strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        self.request.sendall(img_bytes) #<-画像を送信
        #y = bytearray([0x01,0x02,0x03,0x04])
        #print(y)
        #self.request.sendall(y)

if __name__ == "__main__":
    HOST, PORT = '127.0.0.1', 2001

    with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
        server.serve_forever()
        

Kotlin

package xxx

import android.graphics.BitmapFactory
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import java.nio.ByteBuffer

const val MSG_CONNECTION_SUCCESS = 111 // 接続成功
const val MSG_CONNECTION_FAILED = 222  // 接続失敗
const val MSG_IOEXCEPTION = 333        // 例外発生

class MainActivity : AppCompatActivity() {
    private var tcpcom: ComTcpClient? = null
    val ip = "10.0.2.2"
    val port = "2001"  
    private val TAG = MainActivity::class.java.simpleName

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        connect()

        val imgView = findViewById<ImageView>(R.id.imageView)
        val button = findViewById<Button>(R.id.sendButton)
        button.setOnClickListener{
            //todo なんかメッセージ送る
            val sendArray = ByteBuffer.allocate(30)
            sendArray.putInt(0x01020304)
            sendArray.putInt(0x05060708)
            val byteArray = ByteBuffer.allocate(640000)
            try {
                tcpcom?.sendOrReceive { outputStream, inputStream ->
                    outputStream.write(sendArray.array())
                    inputStream.read(byteArray.array())

                    //todo whileで受信し続ける必要がある
                    /* ByteArrayOutputStream
                    BitmapFactory.decodeByteArray()*/

                    //println("受信メッセージ:"+byteArray.getInt())
                    val img = BitmapFactory.decodeByteArray(byteArray.array(),0,byteArray.array().size)
                    print(img)
                }
                //imgView.setImageBitmap(img)
            }
            catch(e: Exception){
                Log.d(TAG,"Failed...")
            }
        }

        val channel = Channel<Int>()
        GlobalScope.launch(Dispatchers.Main) {
            when (channel.receive()) {
                MSG_CONNECTION_SUCCESS -> {
                    Log.d(TAG,"Success!")
                }
                MSG_CONNECTION_FAILED -> {
                    Log.d(TAG,"Failed...")
                    // エラー処理
                }
                MSG_IOEXCEPTION -> {
                    Log.d(TAG,"Exception!")
                    //エラー処理
                }
            }
        }
    }

    fun connect(){
        val channel = Channel<Int>()
        if (!ip.isEmpty() && !port.isEmpty()) {
            tcpcom = ComTcpClient(ip, port.toInt(), channel)
            tcpcom?.connect()
        }
    }
}

Client側でinputStreamをループさせ続けることで大きなサイズの画像も受信することができた。1MB程度まで確認。
なおServer側は画像のbytearrayを一度で投げている。
受信もといループの画像サイズは手動設定。

            try {
                tcpcom?.sendOrReceive { outputStream, inputStream ->
                    outputStream.write(sendArray.array())
                    while(byteSize<1400000) {
                        inputStream.read(byteArray.array())

                        allByteArray.write(byteArray.array())
                        byteSize += 1024


                    }
                    //println("受信メッセージ:"+byteArray.getInt())
                    val img = BitmapFactory.decodeByteArray(allByteArray.toByteArray(),0,allByteArray.toByteArray().size)
                    handler.post{
                        imgView.setImageBitmap(img)
                    }
                    print(img)
                }
                //imgView.setImageBitmap(img)
            }

f:id:Rgrayjourney:20210908201954p:plain
handlerを使ってviewの更新もできた。一歩前進。
実際はサイズ調整も必要になりそう。


参考
Python:
qiita.com


Kotlin:
qiita.com

AtCoder Beginner Contest 184 by C

AtCoder Beginner Contest 184 について
C言語での回答です

atcoder.jp

できたもの
A
B

できなかったもの
C
D以降

問題A

#include <stdio.h>

int main(void){
    int a,b,c,d;
    scanf("%d %d", &a,&b);
    scanf("%d %d", &c,&d);

    printf("%d\n", a*d-b*c);

    return 0;
}
方針

行列式の計算式が問題文中に書かれているのでそのまま計算してその結果を出力。

問題B

#include <stdio.h>
#include <string.h>

int main(void){
    int n,x,i;
    char s[200001];
    scanf("%d %d", &n,&x);
    scanf("%s", s);
    int size = strlen(s);

    long long total = x;
    for(i=0;i<size;i++){
        if(s[i]=='o'){
            total++;
        }
        else if(s[i]=='x'&&total!=0){
            total--;
        }
    }

    printf("%lld\n", total);

    
    return 0;
}
方針

得点を計算する変数の初期値をXとし、oなら+1、xなら-1を文字列の最後まで繰り返します。
ただし点数が0の場合にxがきても減らないのでそこは分岐の条件に注意。

問題C

#include <stdio.h>

int abs(int a){
    return a < 0 ? -a : a;
}

int main(void){
    int r1,c1,r2,c2,vr,vc,i;
    scanf("%d %d", &r1,&c1);
    scanf("%d %d", &r2,&c2);

    vr = r2-r1;
    vc = c2-c1;

    int ans = 3;
    if(vr==0&&vc==0){
        ans = 0;
    }
    else if(vr+vc==0||vr-vc==0||abs(vr)+abs(vc)<=3){
        ans = 1;
    }
    //斜め2回移動
    else if((vr+vc)%2==0){
        ans = 2;
    }
    //ブロック2回移動。マンハッタン距離3以下×2->6以下
    else if(abs(vr)+abs(vc)<=6){
        ans = 2;
    }
    //斜め右上+ブロック
    else if(abs(vr-vc)<=3){  
        ans = 2;
    }
    //斜め右下+ブロック
    else if(abs(vr+vc)<=3){
        ans = 2;
    }

    printf("%d\n", ans);

    return 0;
}
方針

f:id:Rgrayjourney:20210610201811j:plain

a+b=c+dという条件で現在のマスの右上がり(傾き正)方向のマスに移動ができ、
a-b=c-dという条件で右下がり(傾き負)方向のマスに移動ができます。(画像上部)
よってそれらを2回組み合わせることで元の座標と偶奇性が一致するすべてのマスに移動ができます。

偶奇性が異なるマスであっても、隣あうマスに移動すればマンハッタン距離は1となり、これは|a-c|+|b-d|≦3(マンハッタン距離が3以下)を満たすので全てのマスに3回以内に移動できます。

・移動が0回
移動先のマスと現在のマスが同じとき

・移動が1回
移動先のマスが、
a+b=c+d   右下がり直線上のマス
a-b=c-d    右上がり直線上のマス
|a-c|+|b-d|≦3  マンハッタン距離が3以下のマス

・移動が2回
右下がりと右上がり1回ずつ
右下がりとマンハッタン距離が3以下
右上がりとマンハッタン距離が3以下
マンハッタン距離が3以下が2回

・移動が3回
上記以外

なおマスの移動については、座標の差が大事なため、最初と最後のマス自体にそんなに意味はない。
vr = r2-r1, vc = c2-c1とすると、(0, 0)から(vr, vc)へ移動するとみなすこともできる。
(今回はそんなに関係なかったけど)

解けなかった原因

移動できる条件から移動できるマスを正確に把握できず、条件わけができなかった。

問題D

まだ

AtCoder Beginner Contest 182 by C

AtCoder Beginner Contest 182 について
C言語での回答

できたもの
A
B
C

できなかったもの
D以降

問題A

#include <stdio.h>

int main(void){
    int a,b;
    scanf("%d %d", &a,&b);

    printf("%d\n", (2*a+100)-b);
    
    return 0;
}
方針

フォローの上限から今のフォローを引けばそれが答え。

問題B

#include <stdio.h>

int main(void){
    int n,i,j;
    int a;
    int count[1001]={0};
    scanf("%d", &n);

    int maxnum = 0;
    for(i=0;i<n;i++){
        scanf("%d", &a);
        for(j=2;j<=a;j++){
            if(a%j==0){
                count[j]++;
            }
        }
        if(a>maxnum){
            maxnum = a;
        }
    }

    int max = 0;
    for(i=2;i<=maxnum;i++){
        //printf("i:%d %d\n", i,count[i]);
        if(count[i]>max){
            max = count[i];
            j = i;
        }
    }

    printf("%d\n", j);
    
    return 0;
}
方針

Nの数が100、Aの上限値が1000と小さいので全探索が利用できます。
Aiまでのすべてのiについて割り切れるかどうかをそれぞれカウントし、カウントが1番多いものが答え。
最大のものが複数ある場合にはどれを出力してもいいので最小の値、つまりmaxの時のiを使いました。

0で割らないようforの始まりは1以上にしましょう!!(本日2回目)

問題C

#include <stdio.h>
#include <stdlib.h>

int main(void){
    int i;
    long long n;
    int s[20],mod[20]={0};
    scanf("%lld", &n);

    int num;
    while(n>0){
        s[num] = n%10;
        mod[s[num]%3]++;
        n /= 10;
        num++;
    }

    int sum = 0;
    for(i=0;i<num;i++){
        sum += s[i];
    }

    if(sum%3==0){
        printf("0\n");
    }
    else if(sum%3==2){
        if(mod[2]>0&&num>1){
          printf("1\n");
        }
        else if(mod[1]>1&&num>2){
          printf("2\n");
        }
        else{
          printf("-1\n");
        }
     }
     else {
       if(mod[1]>0&&num>1){
         printf("1\n");
       }
       else if(mod[2]>1&&num>2){
         printf("2\n");
       }
       else{
         printf("-1\n");
       }
    }    
    return 0;
}
方針

ある数が3で割れるというのは、その数の桁をすべて足して得られる数が3で割りきれることと同じです。
まずはそれを用いて操作しなくても割り切れるかどうかをチェック。

1e18なのでint型では足りないためlong long型を使用します。
10で割ったあまりを随時受け取れば各桁の数字が得られます。
ちなみにここで得た各桁の数字を3で割ったあまりも保持しておき、下の場合分けを満たすか否かで用います。
それらの総和が元の数の全ての桁を足した数になります。

①総和を3で割ったあまりが0
何も消す必要がないので0を出力。

②総和を3で割ったあまりが1
あまりが1の数字を一つ消すか、あまりが2の数字を二つ消せば3で割り切れるようになります。

③総和を3で割ったあまりが2
あまりが2の数字を一つ消すか、あまりが1の数字を一つ消せば3で割り切れるようになります。

ただし、②と③において元々の数の桁数が消す数と同じであれば問題文の条件に反するのでNG。

以下は最初の入力を文字列で受け取るパターン。
後半の処理は同じです。
数値として足すためにintへ変換する事を忘れずに。

#include <stdio.h>
#include <string.h>

int main(void){
    int i;
    char s[20];
    int mod[5]={0};
    scanf("%s", s);
    int size = strlen(s);

    int sum = 0;
    for(i=0;i<size;i++){
        sum += (int)s[i];
        mod[s[i]%3]++;
    }

    if(sum%3==0){
        printf("0\n");
    }
    else if(sum%3==2){
        if(mod[2]>0&&size>1){
          printf("1\n");
        }
        else if(mod[1]>1&&size>2){
          printf("2\n");
        }
        else{
          printf("-1\n");
        }
    }
    else{
        if(mod[1]>0&&size>1){
          printf("1\n");
        }
        else if(mod[2]>1&&size>2){
          printf("2\n");
        }
        else{
          printf("-1\n");
        }
    }

    return 0;
}

問題D

ナイーブなやりかたは思いついたけどまあ当然TLEだな......というところ

#include <stdio.h>

long long max(long long a, long long b){
    return a > b ? a : b;
}

int main(void){
    int n,i,j;
    long long a[200001];
    scanf("%d", &n);

    for(i=0;i<n;i++){
        scanf("%lld", &a[i]);
    }

    long long p = 0; //初めの座標を0とした時の座標
    long long q = 0; //動作i中における最大の座標
    long long r = 0; //全体での最大の座標
    long long x = 0; //一つ移動したあとの座標
    for(i=0;i<n;i++){
        p += a[i];
        q = max(q,p);
        r = max(r,x+q);
        x += p; 
    }

    printf("%lld\n",r);
    
    return 0;
}
方針

a1からaiまで足す動作を動作iとし、その中で値を随時更新。
やっぱり型には注意。

問題E

まだ

AtCoder Beginner Contest 180 by C

AtCoder Beginner Contest 180 について
C言語での回答

atcoder.jp


できたもの
A
B
C

できなかったもの
D以降

問題A

#include <stdio.h>

int main(void){
    int n,a,b;
    scanf("%d %d %d", &n,&a,&b);

    printf("%d\n", n-a+b);
    
    return 0;
}
方針

元々入ってた数(n)からA個を取り出し(-A)、B個入れる(+B)というのを出力。

問題B

#include <stdio.h>
#include <math.h>

double abs(double a){
    return a < 0 ? -a : a;
}

double max(double a,double b){
    return a > b ? a : b;
}

int main(void){
    int n,i;
    double x[100001];
    scanf("%d", &n);

    for(i=0;i<n;i++){
        scanf("%lf", &x[i]);
    }

    //マンハッタン距離
    long long dm = 0;
    for(i=0;i<n;i++){
        dm += abs(x[i]);
    }
    printf("%lld\n", dm);

    //ユークリッド距離
    double de = 0;
    for(i=0;i<n;i++){
        de += x[i]*x[i];
    }
    double d = sqrt(de);
    printf("%.15lf\n", d);

    //チェビシフ距離
    long long dc = 0;
    int maxnum = 0;
    for(i=0;i<n;i++){
        maxnum = max(maxnum,abs(x[i]));
        dc = maxnum;
    }
    printf("%lld\n", dc);


    return 0;
}
方針

マンハッタン距離は絶対値を返す関数を(abs関数)、チェビシェフ距離は大きい値を返す関数を(max関数)つくってやり、それを利用して指示通りに計算。
ユークリッド距離は平方根を計算しないといけないため、math.hを読み込みsqrtを使います。
このとき、表示する小数点の桁数を増やしておかないと誤差の関係で弾かれるようなので要調整。
%.15lf と指定子の前に数字を付けるとその桁だけ表示。

合計が大きくなるので型に注意。
xの型をintにしてると上手くいかないのでdouble型でやりました。

オーバーフローについての解説(この問題の解説についてる補足)
atcoder.jp

問題C

#include <stdio.h>

int main(void){
    long long n,i;
    scanf("%lld", &n);
    long long a1[1000001];
    long long a2[1000001];

    long long j = 0,k = 0;
    for(i=1;i*i<=n;i++){
        if(n%i==0){
            a1[j] = i;
            j++;
            if(i*i!=n){
                a2[k] = n/i;
                k++;
            }
        }
    }

    for(i=0;i<j;i++){
        printf("%lld\n", a1[i]);
    }
    for(i=k-1;i>=0;i--){
        printf("%lld\n", a2[i]);
    }

    return 0;
}
方針

約数全列挙問題。
iからNまでやると10^12で時間が足りなくなるため、積の交換法則をここでも使うとn/2で解けます。
0で割らないようiは1から始めてください!!

0で割ってるのがエラーの原因と気づかず、配列が大きいのがダメか?とmallocで作ったのが下。
mallocでは1e8はいいが1e9あたりからエラーになる(終了コード139で多分メモリ周りがよくない)

#include <stdio.h>
#include <stdlib.h>

int main(void){
    long long n,i;
    scanf("%lld", &n);
    //long long a1[1000001],a2[1000001];
    long long *a1;
    long long *a2;
    a1 = (long long*)malloc(sizeof(long long)*n/2);
    a2 = (long long*)malloc(sizeof(long long)*n/2);

    long long j = 0,k = 0;
    for(i=1;i*i<=n;i++){
        if(n%i==0){
            a1[j] = i;
            j++;
            if(i*i!=n){
                a2[k] = n/i;
                k++;
            }
        }
    }

    for(i=0;i<j;i++){ 
        printf("%lld\n", a1[i]);
     } 
    for(i=k-1;i>=0;i--){
        printf("%lld\n", a2[i]);
    }

    free(a1);
    free(a2);

    return 0;
}

問題D

#include <stdio.h>

typedef long long ll;

int main(void){
    ll x,y,a,b;
    scanf("%lld %lld %lld %lld", &x,&y,&a,&b);

    ll exp = 0;
    while(x<=(x+b)/a&&a*x<y){
        x *= a;
        exp++;
    }

    printf("%lld\n", exp+(y-1-x)/b);
    
    return 0;
}
方針

より多くトレーニングできる方が多く経験値を得られるので、カコモンジムに通う場合とAtCoderジムに通う場合のうち、値が小さいほうを選べばいいです。
ただし、1e18なのでそれを逐次やるとTLEします(した)。

よって上の方針を言い換える必要があります。
まずaを何回するかを決め、残りの回数b回足すことにします。
よってaの回数を求めます。
ある数zについて、z*a <= z+bを計算しますが、これはz <= (z+b)/aと書き換えることができます。

あとはこの条件を満たす回数がaの回数、
bの回数はy未満かつaによって増えた経験値を引いた残りを割れば出ます。

問題E

まだ

各言語での入出力まとめ

いろいろ使ってるとどれがどれだかわからなくなったりするので備忘録。
随時加筆修正予定。

C

//入力
scanf("%d", &a);
//出力
printf("%d\n", a);

C言語は非常にめんどくさいですが入出力時に型ごとのフォーマット指定子が必要です。
www.k-cube.co.jp

また入力時に数値や文字であれば&を付け、文字列であれば付けません。
(その理由はポインタ絡みなのですが、もし気が向けばちゃんと書きます)

横に沢山入力して受け取りたい時はscanf内で横に並べます。

scanf("%d %d %d", &a, &b,&c);

50 100 150と入力するとa=50,b=100,c=150が入ります。

文字列の入出力について
char s[5];
scanf("%s", s);
//s="aeiou"
printf("%c\n", s[0]); //a
printf("%c\n", s[1]); //e
printf("%c\n", s[2]); //i
printf("%c\n", s[3]); //o
printf("%c\n", s[4]); //u

文字列は配列に入れると添え字で文字の指定ができます。

入出力とは話がずれますが、プログラム中で文字を扱う場合に、文字列であれば" "を、文字であれば' 'を使う点も注意。

二次元配列の入出力
char s[2][5];
for(int i=0;i<2;i++){
        scanf("%s", s[i]);
}
//s1="abcde", s2 ="hijkl"

printf("%c\n", s[0][2]); //c
printf("%c\n", s[1][4]); //l

C++

//入力
cin >> a;
//出力
cout << a << endl;

<<の向きに注意。
endlで改行。改行しない場合には付けません。

こちらも横に受け取りたい場合にはどんどん横に伸ばしていきます。

//入力
cin >> a >> b >> c;

C#

//入力
Console.ReadLine();
//出力
Console.WriteLine();

Python

//入力
input()
//出力
print()

横に入力する場合にはmapを使います。

a,b,c=(int, input().split())

出力時に改行しない方法についてはこちらに色々載っています。
teratail.com

JavaScript

おまけ。最近触ってないのと他の言語で勝手が違うのでこれでいいかはちょっとわかんない(違ってたらごめんなさい)

//入力
document.getElementById('');
//出力
Console.log();

AtCoder Beginner Contest 071 by C

AtCoder Beginner Contest 071 について
C言語での回答です

atcoder.jp

できたもの
A
B

できなかったもの
C
D

問題A

#include <stdio.h>

int abs(int n){
    return n < 0 ? -n : n;
}

int main(void){
    int x,a,b;
    scanf("%d %d %d", &x,&a,&b);

    if(abs(a-x)<abs(b-x)){
        printf("A\n");
    }
    else{
        printf("B\n");
    }
    
    return 0;
}
方針

xからa,bまでの距離の絶対値が小さい方が答え。
絶対値を求めるのはabs関数で、差が負だった場合にマイナスを付けて正にします。

問題B

#include <stdio.h>
#include <string.h>

int main(void){
    int i;
    char s[100001];
    scanf("%s", s);
    int size = strlen(s);

    int count[26] = {0};
    for(i=0;i<size;i++){
        count[s[i]-97]++;
    }

    for(i=0;i<26;i++){
        if(count[i]!=0){
            continue;
        }
        else{
            printf("%c\n", i+97);
            return 0;
        }
    }

    printf("None\n");
    
    return 0;
}
方針

出てきたアルファベットを配列でカウントすることで文字列に現れるか否かを判断します。
出力する際にやりやすいかと思い文字コードで判断しました。
最初にカウントが0である文字のアルファベットを出力します。
全部カウントがあった場合はfor文を抜けるのでNoneを出力します。

問題C

#include <stdio.h>
#include <stdlib.h>

void merge_sort(int array[], int left, int right){
    int i,j,k,mid;
    int *work;

    if(left >= right){
        return;
    }

    work = (int*)malloc(sizeof(int)*right+1);

    mid = (left+right)/2;
    merge_sort(array,left,mid);
    merge_sort(array,mid+1,right);

    for(i=mid;i>=left;i--){
        work[i] = array[i];
    }
    for(j=mid+1;j<=right;j++){
        work[right-(j-(mid+1))] = array[j];
    }
    i = left; 
    j = right;

    for(k=left;k<=right;k++){
        if(work[i]<work[j]){
            array[k] = work[i++];
        }
        else{
            array[k] = work[j--];
        }
    }    
    free(work);

}

int main(void){
    int n,i,j;
    int max1 = 0,max2 = 0; 
    int a[100001];
    scanf("%d", &n);

    for(i=0;i<n;i++){
        scanf("%d", &a[i]);
    }

    merge_sort(a,0,n-1);
  
    for(i=n-1;i>=0;i--){
        //同じ数字が2つあるか
        if(a[i]==a[i-1]){
            max1 = a[i];
            //同じ数字が4つあるか
            if(a[i-1]==a[i-2]&&a[i-2]==a[i-3]){
                printf("%lld\n", (long long)max1*(long long)max1);
                return 0;
            }
            break;
        }
    }

    //別に2つ以上ある数字があるか
    for(j=i-1;j>=0;j--){
        if(a[j]==max1){
          continue;
        }
        if(a[j]==a[j-1]){
            max2 = a[j];
            break;
        }
    }

    printf("%lld\n", (long long)max1*(long long)max2);


    return 0;
}
方針

まず4つ以上同じ数字があるか、2~3個ある数字が2組あれば長方形(正方形を含む)が作れます。
面積を最大にするにはその条件を満たす数字のうちより大きいものを使えばいいです。

大きい数字を使うためにまず配列を昇順でソートします。
数字の数が~10^5と大きいのでマージソートとやらを使う模様。

詳しくは↓
qiita.com

ソートしたら配列の後ろからまず同じ数字が2つあるか調べます。
2つ並んでいるものがあればついでにあともう2つないか調べてもしあれば4つあるということで正方形を作ります。
なければ次に大きくて2つ以上ある数値をしらべ、見つかればその2種類をかけ合わせます。

long long 型を用いることに注意。

問題D

#include <stdio.h>

#define divide 1000000007

int main(void){
    int n,i,t;
    long long ans = 1;
    int flag;
    char s1[53],s2[53];
    scanf("%d", &n);
    scanf("%s", s1);
    scanf("%s", s2);
    
    if(s1[0]==s2[0]){
        ans *= 3;
        flag = 0;
        t = 1;
    }
    else{
        ans *= 6;
        flag = 1;
        t = 2;
    }

    for(i=t;i<n;i++){
        if(flag==0){
            if(s1[i]==s2[i]){
                ans *= 2;
                ans %= divide;
                flag = 0;
            }
            else{
                ans *= 2;
                ans %= divide;
                flag = 1;
                i++;
            }
        }
        else{
            if(s1[i]==s2[i]){
                flag = 0;
            }
            else{
                ans *= 3;
                ans %= divide;
                flag = 1;
                i++;
            }
        }
    }

    printf("%lld\n", ans%divide);

    return 0;
}
方針

文字列を左から順にチェック
以下のパターン①とパターン②の組み合わせでできています。

パターン①:一種類を縦に並べる
t
t

パターン②:二種類を横に並べる
ww
yy

これらについて、
パターン①
先頭:3通り
左隣がパターン①:2通り
左隣がパターン②:1通り
パターン②
先頭:6通り
左隣がパターン①:2通り
左隣がパターン②:3通り

これをかけ合わせていくと解が得られます。

割ったあまりを利用することを忘れずに。
今回は掛け算なので気にせず随時割っていけばOK