AtCoderで青色になりました
苦節1年4ヶ月、ようやく青色になりました
おぉぉっっぉっっぉぉぉぉおおあ
— sashiming🐟 (@sashiming_syumi) 2020年6月21日
SashimingさんのAtCoder Beginner Contest 171での成績:306位
パフォーマンス:2030相当
レーティング:1566→1621 (+55) :)
Highestを更新し、2 級になりました!#AtCoder #ABC171 https://t.co/Kf8jllJ2sE pic.twitter.com/IIevL2ye4u
やったこと
DP力が多少必要なのでこれで鍛えましょう JOI 過去問もおすすめ
- こどふぉの問題を解く
こどふぉは単純に問題数がバカ多いので練習がたくさんできます
生活習慣ぶっ壊れる覚悟さえあればこどふぉのレートは勝手に上がります
- レートを気にせず楽しむ
「レートを冷やすは、いとをかし」の精神でやりましょう どうしても冷えてかなしいときはうしさんの -285 を見て笑顔になりましょう
すごいことになっている pic.twitter.com/Ti9gb7Ztbs
— 🐮😁 (@ei1333) 2020年3月3日
おわり
青色達成おめでとう
黄色目指して頑張りましょう
JOI 2019/2020 2次予選 感想
ABCDの4完でした
以下、解いた順に書いていきます
A問題 - ポスター
一瞬ん?となったけど各方向について全部のマスを見るだけです、はい
ところで謎関数書いてないか?
C問題 - 桁和
桁和の文字列見た瞬間身構えたけど、そんなに難しくなかった
から それぞれについて、もとの数と一回操作した後の数を UnionFind 上で unite させる
すると、 を含む集合の size をとるだけで答えが求められる
集合サイズを求められる UF のライブラリを持ってたら一瞬で解ける
ところで桁和求める関数の実装がスマートじゃなくない?めっちゃ緊張してますね
B問題 - いちご
Cよりは難しいと思う
最遠点には出来るだけ早く到着しないとダメそうなお気持ちを察したので、とりあえずスタート直後は最遠点に直行
帰りに残りのイチゴを回収していく感じ
そしてクソ実装、まあ通ったからヨシ!
D問題 - テンキー
脳死桁 DP 笑 これしか浮かばなかった
多分 10 桁で十分だろうな~、でも一応 15 桁取っとくか~ という気持ちで投げたけど、14 桁で落ちるケースがあるらしくびっくり あぶねー
ところで 15 桁で十分な証明をしていませんよね?ダメですねえ
以下実装 (dist 配列は各キー間の移動距離を埋め込んだ)
ちなみに提出結果はこの通り 時間ギリギリだけど剰余演算最適化したらはやくなりそう
E問題 - じゃんけん式
なにこれ笑
構文解析をやったことないので Google 先生の力で 20 点取ろうとしたものの、めっちゃバグりまくった うく
終了後に見返したら、20 点狙いより満点狙いのほうがやりやすいと感じた 最初から諦めるのはやめましょう
おわり
多分 2 次予選通過ですね、対戦ありがとうございました
ABC121 D - XOR World メモ
XORの性質とか知らないのでアでした、あと D 解けてる人多くてビックリ
問題概要
以上 以下の全ての整数の XOR 和を求めよ。
- 制約:
問題リンクは こちら から
解説
ここからは と の XOR を で表すことにします。
言い換え
XOR の性質として、
- 同じ数の XOR は0 :
があります。
問題上の は、この性質を用いれば
これは 以上 以下の部分で数がダブり、その部分における XOR 和が 0 になるからです。
よって、 から までの XOR 和は、 から までの XOR 和と から までの XOR 和の XOR と等しくなります。
0からXまでのXOR和を求める
以上のことから、 から までの XOR 和と から までの XOR 和さえ求めればよいです。
ここで実験。適当に で を求めると
x bin f(0,x) ------------------- 0 0000 0000 1 0001 0001 2 0010 0011 3 0011 0000 4 0100 0100 5 0101 0001 6 0110 0111 7 0111 0000 8 1000 1000 9 1001 0001 10 1010 1011 11 1011 0000 12 1100 1100 13 1101 0001 14 1110 1111 15 1111 0000
実験より、が
- のとき:
- のとき:
- のとき:
- のとき:
となります。これで が求められるようになったので、この問題は解けました。
#include<bits/stdc++.h> using namespace std; typedef long long ll; int main(void){ ll a, b; cin >> a >> b; a--; ll xa, xb; if(a % 4 == 0) xa = a; else if(a % 4 == 1) xa = 1; else if(a % 4 == 2) xa = a+1; else xa = 0; if(b % 4 == 0) xb = b; else if(b % 4 == 1) xb = 1; else if(b % 4 == 2) xb = b+1; else xb = 0; cout << (ll)(xa^xb) << endl; return 0; }
ABC110 D - Factorization メモ
逆元の mod のとり方を知ったので自分用にメモ。
問題はこちらから
大まかな解き方
問題を解く上での考え方は単純。 の素因数の種類の数を として、
と素因数分解されるとします。
それぞれの素因数 が 個あるので、これらを 個のグループに分配します(0個のグループがあってもよい)。この分け方の総数は です(1列に並んだ 個の ◯ を 本の区切りで分けると考えればよいです、下図参照)。
この値を のそれぞれについて全てかけ合わせたものを 1e9+7 (= 1000000007)で割った余りが答えです。
要するに
が答えです。
問題点
を愚直に求めて mod をとろうとしても、値が long long でも扱えないほどクソでかくなるときが出てきます。 の時点でムリです。これを解決するにはどうすればいいでしょうか?
解法
の定義に除算が入っているので、通常では計算途中に mod 計算をすることができません。しかし、次のようにすれば除算でも mod をとることが出来ます。
ここからは の mod をとることについて考えましょう。
なので、 の mod をとることは の mod をとることと同義です。(ちなみに のことを の逆元と呼びます)
ここで、フェルマーの小定理より
は互いに素
この両辺に をかけて、
は互いに素
(p = 1e9+7 であり、1e9+7 は素数なので、a が p の倍数でない限り a, p は互いに素になります。今回の場合、制約上 a が p の倍数になることはないためフェルマーの小定理が適用できます)
以上より、 の答えを で割った余りは、 を で割った余りに等しいです。
の定義は次のように変形されます。
剰余を求めたいので、 を利用して
コード解説
ACコードは以下の通りです。(そこそこ長いです。もっと洗練できるかも)
#include <bits/stdc++.h> using namespace std; #define int long long // 簡略化のため longlong を int に const long long MOD = 1e9+7; int fact[150000], revfact[150000]; // fact[i]はiまでの階乗、revfact[i]はiまでの階乗の逆元 queue<int> prime_count; int powmod(int a, int p){ int res = 1, tmp = a; while(p != 0){ if(p % 2) res = (res*tmp) % MOD; tmp = (tmp*tmp) % MOD; p /= 2; } return res; } void factmod(){ fact[0] = revfact[0] = 1; for(int i = 1; i < 150000; i++){ fact[i] = (fact[i-1] * i) % MOD; revfact[i] = (revfact[i-1] * powmod(i, MOD-2)) % MOD; } } int nCrmod(int n, int r){ // nCr % MOD return (((fact[n] * revfact[r]) % MOD) * revfact[n-r]) % MOD; } signed main(){ // ここの signed を int にするとCEします int N, M; cin >> N >> M; for(int i = 2; i <= sqrt(M); i++){ // 素因数分解して指数を queue に突っ込む if(M % i == 0){ int cnt = 0; while(M % i == 0){ M /= i; cnt++; } prime_count.push(cnt); } if(M == 1) break; } if(M != 1) prime_count.push(1); // Mが素数の場合はqueueに1だけが入ることになります factmod(); int ans = 1; while(!prime_count.empty()){ int bi = prime_count.front(); ans *= nCrmod(N+bi-1, bi); ans %= MOD; prime_count.pop(); } cout << ans << endl; return 0; }
一部分ずつ解説していきます。
int powmod(int a, int p){ int res = 1, tmp = a; while(p != 0){ if(p % 2) res = (res*tmp) % MOD; tmp = (tmp*tmp) % MOD; p /= 2; } return res; }
(mod 1e9+7) を計算します。
今回の場合、 なので、通常の累乗の求め方(計算量 )ではTLEしてしまいます。
そこで、繰り返し二乗法という手法を使います。
例として について考えることにしましょう。 は次のように変形されます。
次に、指数である を2進数で表すと です。ここで、ビットが立っているところは を分解した時のそれぞれの指数に対応しています。
したがって、 は をそれぞれ計算すると求められます。
(僕の説明はわかりにくいので こちら を参考にすると良いと思います)
繰り返し二乗法を使うと、 が と高速に求められます。
void factmod(){ fact[0] = revfact[0] = 1; for(int i = 1; i < 150000; i++){ fact[i] = (fact[i-1] * i) % MOD; revfact[i] = (revfact[i-1] * powmod(i, MOD-2)) % MOD; } }
fact[i] は i までの階乗 (mod 1e9+7)の配列、revfact[i] は i までの階乗の逆元 (mod 1e9+7) の配列です。制約上、150000 ぐらいまで計算しておけば十分です。十分すぎるくらいです。
そのままの階乗の計算は特に言うことはありませんが、 の階乗の逆元の計算は
で求められます。
int nCrmod(int n, int r){ return (((fact[n] * revfact[r]) % MOD) * revfact[n-r]) % MOD; }
nCr (mod 1e9+7) を求めています。nCr の定義の通りに計算しています。
for(int i = 2; i <= sqrt(M); i++){ if(M % i == 0){ int cnt = 0; while(M % i == 0){ M /= i; cnt++; } prime_count.push(cnt); } if(M == 1) break; } if(M != 1) prime_count.push(1); // Mが素数の場合はqueueに1だけが入ることになります
を素因数分解し、指数のみを queue に突っ込んでいます(指数しか計算に利用しないためです。素因数分解をライブラリ化する場合は map で素因数を key にしておくとよさそう)。
の素因数分解では、 から までを調べれば十分です( が合成数の場合、 より大きい素因数は存在しないため)。 が素数かそうでないかは、for文を抜けた後の が であるかどうかで判断できます。
さいごに
今回は「フェルマーの小定理+繰り返し二乗法」で解きましたが、他にも解法はあります。
フェルマーの小定理+繰り返し二乗法 で逆元の mod をとる手法はよく出てくるので、ライブラリ化しておくと良いかも。
例えばこの問題だとライブラリコピペしてえい!するとささっと解けてしまいます。
atcoder.jp
<参考にしたサイト>
AtCoderで水色になりました
競プロを本格的に始めてから8ヶ月ほど。ついにAtCoderで水色になりました!!
水色になっちゃった!!!
— 🍏さしみもち💎´ (@sashiming_syumi) 2019年1月20日
すぐ緑落ちしそう pic.twitter.com/aLuYUmARop
ということで、自分が水色になるまでにやったことをまとめます。
AtCoderで水色になるまでにやったこと
- 300点を安定して解けるようにする
正直、ABCで300点までを早解きすれば水色になれます。なので、まず300点を安定してAC出来るぐらいまで精進すると良いです。 ちなみに僕はまともに400点を解けません。
- Unratedコンでもできるだけ参加する
Unratedのコンテストではレートが変動しないので参加しない人も多いと思いますが、触れる問題数も増えるのでできるだけ参加しましょう。
- ばちゃこんに参加する
AtCoder Virtual Contestでコンテストがしょっちゅう開かれています。これに参加することで他人と競いながら精進できるので、ばちゃるのも良さそう。
必要なテク
- 全探索系
ABC-Cでは、アルゴリズムを使うとしても全探索で済むことが多いように感じます(たまにDPとか出てきますが)。全探索・DFS・BFS・bit全探索ぐらいは流石に押さえておきましょう。DPもある程度押さえておくべきでしょう。
- 累積和
これを使うと数列のある区間の総和が で求まるので、なかなか使えるテクニックであります。
基本的なSTLは使えるようにしときましょう。具体的には、vector, map, set, queue, stack。STLを使わないと実装が めんどくさくなるorできない 問題は多いです。
- 己の実装力
実装で殴る問題があったりするので、実装力はある程度磨いておきましょう。実装ミスでWAったらシャレにならないからね。
- ひらめき
重要。難しい問題でも、違う角度で見ればかなり簡単になることが多々あります。
さいごに
他にも二分探索とかは押さえていていて損はないですが、ざっとこんな感じ。大切なのはやはり解いた問題量です。経験の量が物を言います。
これからも精進を重ねて、もっと上の色にまで昇格したい。
もちっ
(2019.1.29追記) 緑落ちは一瞬。しばらくここらへんでレート停滞しそう...?
JOI 2018/2019 予選 感想
はじめに
2回目のJOI参加。去年初めて参加したときはお試し感覚でしたが、今回はそこそこ真面目にやりました。
点数は321点 (100+100+100+15+0+6)。来年は予選通過してみたいですね。
A問題
変な考え方して初っ端から2WAしたのでB, Cから解いて戻ってきた。その後にもう1WAしてようやくAC。結局変な考え方のままACしました。
1日目から毎日ログインすれば最善ですよね。当たり前ですね。
B問題
やるだけ。国語力のおはなし。
C問題
左から"ox"または"xo"となるものを順々に探せばいいです。 ダブって数えてしまうミスに注意。
D問題
下から順番に水位を上げていって、それぞれに島の数を求めて部分点。 島の数を求めるときの水位は1刻みで考えると明らかに駄目なので島の高さの種類で考える。
満点解法は座圧とか聞きましたが、座圧知りません。精進(しなさい)。
E問題
な ぜ 全 探 索 を 怠 っ た
全探もやらない怠慢さで10点を逃しました。
F問題
next_permutationなりを使って順列を作って、条件に合うものの個数を求める。 その後、同じ国の選手を区別するので の階乗をそれぞれかけて で割った余りを出力して部分点。
ちなみに本番中は をそのままかけて求めてしまっていましたが(そのせいで入力例3の出力が4754じゃなくて6192になった)、小課題1の制約が なので助かりました。
まとめ
来年こそ予選通過したい!
なので全力で精進あるのみ。