競プロをしながら、節約と株式投資でセミリタイアを目指す東大卒のブログ

東大卒でメーカー勤務の私がセミリタイアするために投資や競プロを頑張っていこうという趣旨で始めたブログです。独身男性です。お金の大切さや今後の生き方も併せて伝えられたらと思います。

ABC171で書いたコード

using System;
using System.Numerics;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Collections;

namespace debug
{
    class main
    {
        static void Main(string args)
        {
            //問題クラスを展開
            ProgramE a = new ProgramE();
            a.main();//実行する

        }

    }
    //ABC170
    class ProgramA
    {
        public void main()
        {
            //入力
            string s = Console.ReadLine();

            //int型で96より大きければ小文字、それ以外は大文字
            if (s[0] > 96)
                Console.WriteLine("a");
            else
                Console.WriteLine("A");

        }
    }

    class ProgramB
    {
        public void main()
        {
            //入力
            string s = Console.ReadLine().Split(' ');
            int n = int.Parse(s[0]);
            int k = int.Parse(s[1]);

            string t = Console.ReadLine().Split(' ');
            int a = new int[n];
            for (int i = 0i < ni++)
                a[i] = int.Parse(t[i]);

            //aをソート
            Array.Sort(a);

            int ans = 0;
            //k番目までを取り出して足す
            for (int i = 0i < ki++)
                ans += a[i];

            //答出力
            Console.WriteLine(ans);


        }
    }

    class ProgramC
    {
        public void main()
        {

            //入力
            long n = long.Parse(Console.ReadLine());
            long n2 = n;

            List<longlist = new List<long>();
            long t = 100;

            while (n2 != 0)
            {
                n2 -= 1;//1引いておく
                list.Add(n2 % 26);//リストに追加
                n2 /= 26;//26で割る
            }

            //後ろから取り出していく
            for (int i = 0i < list.Counti++)
            {
                Console.Write*1;
            }

        }
    }

    class ProgramD
    {
        public void main()
        {

            //入力
            long n = long.Parse(Console.ReadLine());
            string s = Console.ReadLine().Split(' ');


            long a = new long[100000];//100000までの要素数を入れる配列
            //要素数を足し合わせる
            for (int i = 0i < ni++)
                a[long.Parse(s[i]) -1]++;

            //あらかじめ和をもとめる
            long sum = 0;
            for (int i = 0i < ni++)
                sum += long.Parse(s[i]);

            long q = long.Parse(Console.ReadLine());


            //クエリごとに判定
            for (int i = 0i < qi++)
            {
                string t = Console.ReadLine().Split(' ');
                sum -= a[long.Parse(t[0]) - 1] * long.Parse(t[0]);//Bi*a[Bi]を引く
                sum += a[long.Parse(t[0]) - 1] * long.Parse(t[1]);//Ci*a[Bi]を足す
                a[long.Parse(t[1]) - 1] += a[long.Parse(t[0]) -1];//Ciの要素数にa[Bi]分を足す
                a[long.Parse(t[0]) - 1] = 0;//Biの要素数は0になる
                //答え出力
                Console.WriteLine(sum);


            }

           
        }
    }

    class ProgramE
    {

        public void main()
        {
            //入力
            int n = int.Parse(Console.ReadLine());
            string s = Console.ReadLine().Split(' ');
            long[] a = new long[n];

            for (int i = 0i < ni++)
                a[i] = long.Parse(s[i]);

            //入力の排他的論理和(S)を求める
            long sum = a[0] ^ a[1];
            for (int i = 2i < ni++)
                sum = sum ^ a[i];

            //S^aiが答え
            for (int i = 0i < ni++)
                Console.Write((sum ^ a[i]) + " ");

            
        }

        
    }
 
}





*1:char)(list[list.Count - 1 - i] + 'a'

競技プログラミンング~ABC171後半~

おはようございます。しほみんです。

 

前回に引き続きABC171を振り返ります。F問題振り返っている時間なかったので、省きます。コードは金曜日あげます。

 

んまあ今回のD,Eは知っていれば割と問題なく解けたので両方抑えてはおきたいです。

 

地味にこの解説を書いていると理解できている範囲と理解できていない範囲が明確化しますね。おとといのC問題はちょっとあいまいでしたし。こういう復習悪くないと思います。

 

D問題

N個の正の整数列A1,A2,....,Anがあります。

これにQ回操作を続けて行います。

i回目の操作ではBiである要素がすべてCiになる。

このとき、i回目の操作後の要素の和を出力しなさい。

 

 

とりあえず、何回かD問題を触れていると、問題文通り実装すると時間がかかるとわかります。

Biをいちいち数列で見つけてCiに置き換える。そのあと、和を求めるではo(NQ)となってしまって、とんでもない時間がかかります。

 

ここでは、まず基本的な発想はから振り返ってみましょう。

<基本的な発想>

1. 左から見ていったときに、何かしらの別情報に変換する。

2. 該当するものを見たとき、前の保持情報から条件を満たすか確認。満たすときは、情報を追加または変更する。満たさないときは無視する。

3. 確認し終わったら結果を出す。

 

1.として考えると、数列を何かの情報にできないか考えます。

今回操作はBiをCiにするです。つまり、1回の操作では高々1つ数字だけが変わります。

となると、1回の情報ごとで数列全部の値を見る必要がなく、そのBiはいくつあるか?そして、何個Ciになるか?だけわかればよくなります。

 

つまり、数列の取りうる数だけの配列を用意して、要素のどれがいくつあるか見ればよいです。

 

例えば、1,2,4,1,1,3だったら、1が3つ、2,3,4が1つずつあるので

a[1] = 3,a[2] = 1,a[3] =1,a[4]=1として、入れておけばよいです。

 

そうすれば、一回の動作ではa[Bi]の個数を見ればよく、そのあと、a[Ci]にはa[Bi]分足せばよいのです。(これが2の部分ですね)

 

後は和ですが、i×a[i]なのでいちいち足してもよいですが、これも1回の操作ごとにo(200000)となり、時間がかかりすぎちゃいます。

なので、和の計算も工夫が必要です。

これも同じで、1回の計算で(Ci-Bi)×a[Bi]だけ和が変わることに注目します。

つまり、最初に配列を作った後、その和も作っておけば、一回の操作では上記の式で変形すればよいです。

これで、1回の操作の時間はo(1)となり、o(Q)で処理できるので、間に合います。

 

まとめると、

1.数列を要素の要素数の配列に置き換える。また、数列の和を求めておく。

2.和は(Ci-Bi)×a[Bi]を足して、出力。

3,a[Ci]にa[Bi]分足し合わせて格納(a[Ci] += a[Bi])

4. 1-3をQ回繰り返す。

 

 

E問題

猫がN匹(偶数)います。それぞれの猫には整数が書かれています。

猫たちはxorを計算したかったので、自分以外の猫のもつ整数のxorを求めました。

このとき、それぞれの猫の整数を求めなさい。

 

 

典型問題らしいですが、排他的論理和を知っていれば問題を知らなくても導出は難しくないです。排他的論理和が分からないと厳しいかなって感じです....。

排他的論理和について

AとBを2進数で表しそれぞれの桁を見たとき、2つとも同じ数なら0、そうでなければ1とします。

例えば、4+5なら100+101となり、001で、1となります。

 

今回知っているべき知識はこれです。(排他的論理和は便宜上+で表します)

・排他的論理和は交換法則が成り立つ。A+(B+C) = (A+B)+C

・A+A = 0である。

 

まずは、猫が求めた排他的論理和をai,それぞれの猫の整数をbiとします。

するとaiはbi(i = 1 ~ N、ただしi = iを除く)の和です。

 

ここで、S=a1+a2+a3+....+anを考えます(+は排他的論理和)。

このとき、biでの表記を考えると、実は、biはすべてN-1回出てきます。交換法則が成り立つので好きなように動かせます。

よって、A+A=0という法則と、N-1は奇数から、

S=b1+b2+b3+...+bnが成り立ちます。

 

最後に、a1+Sを考えます。

このとき、a1はb2からbnまでを持っています。

よって、a1+Sはb1が1つ、b2からbnまでを2つずつ持った排他的論理和になります。

後ろの項は消えるので、a1+S=b1となります。

 

よってまとめると、

1, 入力数列の排他的論理和を求める。(Sとする)

2, aiとSの排他的論理和をもとめて出力で終わりです。

ちなみにAとBの排他的論理和はA^Bで求められます。

 

最後に....

正直Cに比べたら、DとEの方が気が付きやすいセットだったなあって感じです。

数学が苦手でもDをきちんと押さえていけばそこまで大失敗でもないのかなあって感じです。まあそこばかりは慣れかもです。

 

ではでは。